diff options
author | peter <peter@FreeBSD.org> | 1997-05-15 22:46:24 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1997-05-15 22:46:24 +0000 |
commit | 4667046756d60c63ed43cb725758ef8379aaaaa2 (patch) | |
tree | c5293bcfb984fb98fcd2185e1d95dfcf7cabc691 /contrib | |
parent | a39eb136c4e6f8f1305f74bec60afd76e7381993 (diff) | |
parent | 4f40fe8334ad5f056e1d9105f23fe7ac859c39ba (diff) | |
download | FreeBSD-src-4667046756d60c63ed43cb725758ef8379aaaaa2.zip FreeBSD-src-4667046756d60c63ed43cb725758ef8379aaaaa2.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r25839,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib')
151 files changed, 36934 insertions, 11829 deletions
diff --git a/contrib/cvs/BUGS b/contrib/cvs/BUGS index fc6af32..2a1c860 100644 --- a/contrib/cvs/BUGS +++ b/contrib/cvs/BUGS @@ -1,15 +1,50 @@ -To report bugs send mail to bug-cvs@prep.ai.mit.edu, or run the "cvsbug" -program and fill out the template: +See the README file for information on how to report bugs (and what +will happen to your bug reports if you do). - $ cvsbug +The following is a list of some of the known bugs. It may or may not +be comprehensive. We would dearly love for people to volunteer to +help us keep it up to date (for starters, if you notice any +inaccuracies, please let bug-cvs know as described in README). There +are some other reported bugs in MINOR-BUGS; the difference, at least +in theory, is that those bugs are less serious. -The "cvsbug" program is installed in the same location as the "cvs" -program. If your installation failed, you may need to run "cvsbug" -directly out of the "src" directory as "src/cvsbug.sh". This is also -the procedure for submitting suggested changes to CVS--note that all -submitted changes may be distributed under the terms of the GNU Public -License, so if you don't like this, don't submit them. +* For platform-specific information (in some cases including known +bugs), see README.VMS, windows-NT/README, or os2/README. There is no +similar file for the unix-like operating systems (not yet, at least). +This file also might contain some platform-specific bugs. + + +* Some people have reported seeing the message "dying gasps from %s +unexpected" (where %s is the name of your server) when using +client/server CVS. One person reported that this had to do with using +pserver and trying to run a program not in the PATH (which is set up +by inetd, I think) from one of the *info scripts. But noone has +carefully tracked this down (is it caused by something in the server +writing to stdout or stderr when it shouldn't? But then wouldn't the +"dying gasps" message be preceded by "warning: unrecognized response +`%s' from cvs server"?). + + +* "make remotecheck" sometimes fails on test 187a3 with + cvs server: in directory .: + cvs [server aborted]: *PANIC* administration files missing +This does not happen every time. (-kingdon, Nov 96, Red Hat linux 3.0.3). + + +* The -m option to "cvs add" does not work with client/server CVS. +CVS will accept the option, but it won't actually set the +file's description. + + +* cvs update walks into a user's work directory if there's a directory + of the same name in the repository even if the user's directory + doesn't yet have a CVS admin sub-directory. This can greatly confuse + users who try to add the same directory at nearly the same time. + + +* 'cvs admin' dumped core when files were missing from working directory + (and from the repository)? * `cvs checkout -d nested/dir/path <module>' just doesn't work. The @@ -17,9 +52,23 @@ License, so if you don't like this, don't submit them. however. -* CVS leaves .#mumble files around when a conflict occurs. (Note: - this is intentional and is documented in doc/cvs.texinfo. Of course - whether it is a good idea is a separate question). +* The following bug was reported against CVS 1.9: + + Create a module named test with a file named test in it. + + cactus:sfavor> cvs get test + cvs checkout: Updating test + U test/test + cactus:sfavor> cd test + cactus:sfavor> cvs get test + cvs checkout: cannot chdir to test: Not a directory + cvs checkout: ignoring module test + Exit 1 + cactus:sfavor> cvs update + cvs update: Updating . + rcs.c:2139: failed assertion `rev == NULL || isdigit (*rev)' + Abort (core dumped) + Exit 134 * pcl-cvs doesn't like it when you try to check in a file which isn't @@ -27,6 +76,36 @@ License, so if you don't like this, don't submit them. what pcl-cvs is looking for. +* From: billr@mpd.tandem.com (Bill Robertson) + Subject: Problem with rtag and the -D option + Date: Fri, 17 Mar 1995 10:53:29 -0600 (CST) + + I have been trying to use the -D option to specify a date for tagging, but + rtag does not recognize the -D option. It is documented to do so and I've + tested the use of -D with cvs update and cvs diff and it works fine there. + +* Defining RELATIVE_REPOS is said to not work with client/server CVS. + +* From: "Charles M. Hannum" <mycroft@ai.mit.edu> + To: info-cvs@prep.ai.mit.edu + Subject: Still one more bug + Date: Sat, 25 Feb 1995 17:01:15 -0500 + + mycroft@duality [1]; cd /usr/src/lib/libc + mycroft@duality [1]; cvs diff -c2 '-D1 day ago' -Dnow + cvs server: Diffing . + cvs server: Diffing DB + cvs [server aborted]: could not chdir to DB: No such file or directory + mycroft@duality [1]; + + `DB' is an old directory, which no longer has files in it, and is + removed automatically when I use the `-P' option to checkout. + + This error doesn't occur when run locally. + + P.S. Is anyone working on fixing these bugs? + + * From: Roland McGrath <roland@gnu.ai.mit.edu> To: Cyclic CVS Hackers <info-cvs@prep.ai.mit.edu> Subject: weird bug @@ -119,125 +198,6 @@ License, so if you don't like this, don't submit them. ls: cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978/csu/cvs-serv28978/ctype/cvs-serv28978/dirent/cvs-serv28978/elf/cvs-serv28978/gnu/cvs-serv28978/gnulib/cvs-serv28978/grp/cvs-serv28978/hurd/cvs-serv28978/hurd/hurd/cvs-serv28978/inet/cvs-serv28978/inet/arpa/cvs-serv28978/inet/netinet[...]/cvs-serv28978/posix/cvs-serv28978/posix/glob/cvs-serv28978/posix/gnu/cvs-serv28978/posix/sys/cvs-serv28978/protocols/cvs-serv28978/pwd/cvs-serv28978/resolv/cvs-serv28978/resolv/arpa/cvs-serv28978/resolv/sys/cvs-serv28978/resource/cvs-serv28978/resource/sys/cvs-serv28978/rpc/cvs-serv28978/setjmp/cvs-serv28978/signal/cvs-serv28978/signal/sys/cvs-serv28978/socket/cvs-serv28978/socket: File name too long cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978/csu/cvs-serv28978/ctype/cvs-serv28978/dirent/cvs-serv28978/elf/cvs-serv28978/gnu/cvs-serv28978/gnulib/cvs-serv28978/grp/cvs-serv28978/hurd/cvs-serv28978/hurd/hurd/cvs-serv28978/inet/cvs-serv28978/inet/arpa/cvs-serv28978/inet/netinet[...]/cvs-serv28978/posix/glob/cvs-serv28978/posix/gnu/cvs-serv28978/posix/sys/cvs-serv28978/protocols/cvs-serv28978/pwd/cvs-serv28978/resolv/cvs-serv28978/resolv/arpa/cvs-serv28978/resolv/sys/cvs-serv28978/resource/cvs-serv28978/resource/sys/cvs-serv28978/rpc/cvs-serv28978/setjmp/cvs-serv28978/signal/cvs-serv28978/signal/sys/cvs-serv28978/socket/cvs-serv28978: - -* From: billr@mpd.tandem.com (Bill Robertson) - Subject: Problem with rtag and the -D option - Date: Fri, 17 Mar 1995 10:53:29 -0600 (CST) - - I have been trying to use the -D option to specify a date for tagging, but - rtag does not recognize the -D option. It is documented to do so and I've - tested the use of -D with cvs update and cvs diff and it works fine there. - - -* We need some version numbers really badly. Are there some - (and Charles Hannum is just not including them in his reports), or do - we simply have no reliable way to distinguish between the various - versions of rCVS people on the list are running? - - Now that I think of it, version numbers present a problem when - people can update their sources anytime and rebuild. I think the - solution is to increment a minor version number *every* time a bug is - fixed, so we can identify uniquely what someone is running when they - submit a report. This implies recording the version increments in the - ChangeLog; that way we can just look to see where a particular version - lies in relation to the flow of changing code. - - Should we be doing same with Guppy? I guess not -- it's only - important when you have people who are updating directly from your - development tree, which is the case with the remote-cvs folks. - - Thoughts? - - -* (Charles Hannum <mycroft@ai.mit.edu>) has these bugs: - - I just tossed remote CVS at a fairly large source tree that I already - had, and noticed a few problems: - - 1) server.c assumes that /usr/tmp is a valid default for the place to - store files uploaded from the client. There are a number of systems - that now use /var/tmp. These should probably be detected by autoconf. - - 2) The server deals rather ungracefully with the tmp directory - becoming full. - - 3) There's some oddness with relative paths in Repository files that - causes the directory prefix to be added twice; e.g. if I have CVSROOT - set to `machine:/this/dir', and I try to update in a directory whose - Repository file says `src/bin', the server looks in - `/this/dir/machine:/this/dir/src/bin'. - -* From: "Charles M. Hannum" <mycroft@ai.mit.edu> - To: jimb@duality.gnu.ai.mit.edu, roland@duality.gnu.ai.mit.edu - Subject: Serious flaw in remote CVS - Date: Wed, 22 Feb 1995 20:54:36 -0500 - - I just found a major flaw in the current implementation. Because the - sockets are not changed to non-blocking mode, write(2)s can hang. In - some cases, this happens on both sides at the same time, with the - socket buffers full in both directions. This causes a deadlock, - because both processes are stuck in I/O wait and thus never drain - their input queues. - - Until this is fixed, I can't use it. I'll look at the problem myself - at some point, but I don't know when. - - - From: "Charles M. Hannum" <mycroft@ai.mit.edu> - To: info-cvs@prep.ai.mit.edu - Cc: jimb@totoro.bio.indiana.edu - Subject: Re: forwarded message from Charles M. Hannum - Date: Wed, 22 Feb 1995 22:07:07 -0500 - - FYI, this happened because the tmp directory on the server became - full. Somehow the server started interpreting the files the client - was sending as commands, and started spewing tons of errors. - Apparently the errors are sent with blocking I/O, or something, and - thus allowed the deadlock to happen. - - -* From: "Charles M. Hannum" <mycroft@ai.mit.edu> - To: info-cvs@prep.ai.mit.edu - Subject: Regarding that relative path problem - Date: Thu, 23 Feb 1995 02:41:51 -0500 - - This is actually more serious. If you have `bar.com:/foo' as your CVS - root directory, then: - - 1) When you check things out, the Repository files will contain - `/foo/...' (i.e. without the machine name), which makes little sense. - - 2) If you instead have a relative path, when the Repository file is - read, `bar.com:/foo' is prepended. This is sent to the server, but - confuses it, because it's not expecting the machine name to be - prepended. - - A slightly klugy fix would be to have the client prepend the machine - name when writing a new Repository file, and strip it off before - sending one to the server. This would be backward-compatible with the - current arrangement. - - -* From: "Charles M. Hannum" <mycroft@ai.mit.edu> - To: info-cvs@prep.ai.mit.edu - Subject: Still one more bug - Date: Sat, 25 Feb 1995 17:01:15 -0500 - - mycroft@duality [1]; cd /usr/src/lib/libc - mycroft@duality [1]; cvs diff -c2 '-D1 day ago' -Dnow - cvs server: Diffing . - cvs server: Diffing DB - cvs [server aborted]: could not chdir to DB: No such file or directory - mycroft@duality [1]; - - `DB' is an old directory, which no longer has files in it, and is - removed automatically when I use the `-P' option to checkout. - - This error doesn't occur when run locally. - - P.S. Is anyone working on fixing these bugs? - - * From: Roland McGrath <roland@gnu.ai.mit.edu> To: Cyclic CVS Hackers <info-cvs@prep.ai.mit.edu> Subject: bizarre failure mode diff --git a/contrib/cvs/ChangeLog b/contrib/cvs/ChangeLog index ad187ad..6c4565b 100644 --- a/contrib/cvs/ChangeLog +++ b/contrib/cvs/ChangeLog @@ -1,3 +1,847 @@ +Sun May 11 11:38:03 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README.VMS: Remove information about "direct TCP". Noone has + been complaining about it being broken (the code bitrotted not long + after it was written), nor has anyone complained + that contrib/listener.c was omitted from the distribution (because + it wasn't mentioned in contrib/Makefile.in DISTFILES). If there + is a desire to resurrect such a feature, it should use port 2401 + as now discussed in doc/cvsclient.texi. + +Thu May 8 12:14:40 1997 Larry Jones <larry.jones@sdrc.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: Update MIPS/SGI Irix 6.2 + * TESTS: Add note about TESTDIR and SGI Irix 6's XFS. + +Wed May 7 12:01:21 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TODO: Fix keywords accidentally expanded in previous checkin. + + * TODO: Add item #185, concerning keyword expansion and merges. + +Sun May 4 19:46:03 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README: Replace section on reporting bugs with a reference to + the bug-reporting section in cvs.texinfo. + +Fri May 2 22:50:04 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * BUGS: Remove item about importing binary files; the bug is fixed. + +Sun Apr 27 19:54:34 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: Refer to doc/DIFFUTILS-2.7-BUG. + + * INSTALL: Don't mention GREP; CVS no longer uses it. + + * configure.in: Add comment about --bindir. + +Thu Apr 24 15:21:17 1997 Norbert Kiesel <nk@cosa.de> + + * configure.in (AC_CHECK_FUNCS): added tempnam and mktemp + * config.h.in, configure: Regenerated with autoconf 2.10. + +21 Apr 1997 Jim Kingdon + + * cvsnt.mak: Visual C++, as usual, wants to fiddle with this. + This time it would appear to be chiefly the dependencies. + +Mon Apr 21 01:06:31 1997 Ian Lance Taylor <ian@cygnus.com> + + * NEWS: Document that the client no longer needs an external patch + program. + +Thu Apr 17 14:28:20 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TODO: Combine items 150 and 181 since they are basically the same. + +Tue Apr 15 12:32:26 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * FAQ: The URL of yahoo's Configuration Management category has + changed. As it might change again, just cite their top-level page + rather than the entire URL. + +8 Apr 1997 Jim Kingdon + + * cvsnt.mak: Add windows-NT/sockerror.c. + +Wed Mar 26 15:51:33 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * BUGS: Further note on import -kb bug. + +Tue Mar 25 17:51:32 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs-format.el: Add comment concerning c-label-offset. + +Wed Mar 19 14:06:40 1997 Jim Meyering <meyering@totoro.cyclic.com> + + * configure.in (test for shadow passwords): Use AC_MSG_RESULT + rather than echo, so configure obeys --quiet. + Use yes and no in message rather than yup and nope. + +19 Mar 1997 Jim Kingdon + + * cvsnt.mak: Now Visual C++ wants to add a bunch of dependencies + for the Release configuration as well as the Debug one. Why it + didn't do this before, I have no idea. + +13 Mar 1997 Jim Kingdon + + * cvsnt.mak: Recent changes have added a number of getline.h + dependencies. + +Thu Mar 13 08:43:04 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * configure.in (AC_C_CROSS): Add comment about obsolescence + thereof. + * config.h.in, configure: Regenerated with autoconf 2.10. + +Thu Mar 13 05:50:29 1997 Philippe De Muyter <phdm@info.ucl.ac.be> + + Here are the fixes I needed to make to cvs-1.9 to get it to + compile and successfully pass 'make check' on m68k-motorola-sysv. + * lib/getwd.c (getwd): Added declaration for getcwd(). + * lib/wait.h (WIFSTOPPED et al.): Macro defined if not defined. + * lib/waitpid.c (waitpid): Use wait, not wait3, if !HAVE_WAIT3. + * src/admin.c (admin): Added declaration for getgrnam(). + * src/server.c (fcntl.h): Do not include file twice. Already included + from system.h from cvs.h. + * src/sanity.sh (imported-f*): Renamed from imported-file*, that were + too long for sysv. + * configure.in (wait3): Added to AC_CHECK_FUNCS list. + +Wed Mar 12 14:32:50 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * BUGS: Add "failed assertion `rev == NULL || isdigit (*rev)'" bug. + + * TODO: Remove item 135; this is solved by %v and %V in loginfo. + + * configure.in (AC_CHECK_FUNCS): Don't check for setvbuf; + HAVE_SETVBUF is no longer used. + * config.h, configure: Regenerated with autoconf 2.10. + + * TODO: Add item 184, concerning MD5-based password hash. + Remove item 14, concerning "pathname stripper". I think that was + a reference to the late unlamented strip_path. + +Sat Mar 8 21:22:54 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: NT 4.0 is client and local (like other NT 3.51 & Win95). + +Fri Mar 7 16:51:13 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: Just talked to a NT 4.0 user; add it to the list. + +Sun Mar 2 22:01:23 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Add item about "cvs admin" vs. "cvs admin .". + + * TODO: Remove item #169. It doesn't really explain what an + "archive library" is or in general what the feature they discuss is + supposed to do--I mean, CVS _can_ be used to store .o's, if + that is what they are talking about. + + * TODO: Add item #183, about greater documentation/visiblity for + Entries.Static and CVS/Tag. + + * INSTALL (footnote 5): Add note about how /usr/tmp vs. /var/tmp + shouldn't be an issue anymore + +Thu Feb 20 13:53:19 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: Update Cray entry per mail from John Bowman + <bowman@ipp-garching.mpg.de> + + * configure.in: Add comments about autoconf version. + +Mon Feb 17 09:55:35 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * configure: Regenerated. + +Sat Feb 15 15:37:39 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * configure.in (AC_OUTPUT): Add windows-NT/SCC/Makefile. + +Sun Dec 15 13:12:30 1996 Michael Douglass <mikedoug@texas.net> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Mention "cvs logout". + +1997-02-12 Jim Kingdon + + * cvsnt.mak: Visual C++ seems to want to make some cosmetic + changes (reordering *.obj files), perhaps prodded by "Save + All". I hope that putting in these changes will make it + happy... + +1997-02-11 Jim Kingdon <kingdon@cyclic.com> + + * cvsnt.mak: Replace with version from Visual C++ 4.0. If someone + wants the 2.x one back, I suppose we can put them side by side, + but I won't be able to update the 2.x one any more as I won't be + having access to 2.x. + +Tue Feb 11 16:43:43 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * .cvsignore: Add cvsnt.mdp and cvsnt.ncb. They seem to be files + created by Visual C++ 4.x which were not created by Visual C++ 2.x. + +Tue Feb 4 11:42:30 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: OS/2 port is client only. + + * Rename devel-cvs (which had only been in the repository, not the + distribution) to DEVEL-CVS. Add "Charter for the devel-cvs + mailing list:" heading, "CVS Development Policies" title, and + one-sentence introduction (editorial changes, not run by + devel-cvs). Revise paragraph concerning membership in the list to + reflect policy change to make read-only membership different from + the ability to send to the list (the new wording was approved by + devel-cvs, as was the rename and including it in the + distribution). + * Makefile.in (DISTFILES): Add DEVEL-CVS. + * HACKING: Add "Mailing lists" section. + +Tue Jan 28 10:41:05 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * configure.in: Remove AC_CHECK_SIZEOF; no longer needed with + lib/md5.c changes. + * acconfig.h: Add HAVE_CONNECT. This is needed so that autoheader + 2.10 works; I think this has been broken since 2 Dec 1996. + * config.h.in: Regenerated with autoheader 2.10. + * configure: Regenerated with autoconf 2.10. + + * HACKING: Revise criterion for whether something goes in NEWS + again (now "user-visible change worth mentioning"--the language + from the GNU coding standards). + +Mon Jan 27 23:05:24 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * HACKING: Criterion for whether something goes in NEWS is not + whether it is user-visible; it is whether it is a bugfix or a + feature. + +Tue Jan 21 10:21:53 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: Warn people against pre-5.x RCS; describe how to find + out what version of RCS you have. + +Wed Jan 8 14:50:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in, NEWS, README, TODO, configure.in: Remove CVSid; we + decided to get rid of these some time ago. + +Wed Jan 8 00:17:13 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README (Credits): Refer to NEWS not ChangeLog; the ChangeLog in + question got renamed a bit but ended up as the bottom of the NEWS + file. Eliminate use of first person in a few places where it is + unclear who it refers to. Explicitly say that the lists + of contributors are not comprehensive. + +Thu Jan 2 12:59:45 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README, Makefile.in: Remove paragraph about writing to the Free + Software Foundation at 675 Massachusetts Avenue. (1) They are no + longer at that address; (2) the Free Software Foundation are not + the ones to write to concerning CVS licensing. bug-cvs would be a + more appropriate choice; (3) there is probably little need for + this paragraph anyway. + +Thu Jan 2 09:46:37 1997 Karl Fogel <kfogel@ynu38.ynu.edu.cn> + + * NEWS: mention read-only repository access feature. + +Wed Jan 1 18:47:08 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.spec: Don't include ChangeLog and ChangeLog.zoo in %doc. + There is no point in including them without src/ChangeLog, + src/ChangeLog-96, etc., but more to the point they really belong + in the source distribution rather than a binary distribution anyway. + +Mon Dec 30 16:55:54 1996 Abe Feldman <feldman@harvey.cyclic.com> + + * NEWS: Add entry for changes to checkout command (creating CVS + directory at top of working directory) + +Tue Dec 17 13:13:30 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Add entry for verifymsg. + +Tue Dec 10 19:22:20 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs-format.el: Revise comments to explain how to use it and + general minor tidying of comments. + +Mon Dec 2 13:05:44 1996 Ian Lance Taylor <ian@cygnus.com> + + * configure.in: Don't call AC_CHECK_FUNCS(connect) a second time, + because the value will have been cached; instead, check whether + the library was found with connect defined. + * configure: Rebuild with autoconf 2.12. + +Sat Nov 30 23:04:52 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * BUGS: Add note about mysterious failure in test 187a3. + +Fri Nov 29 10:19:50 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * configure.in (AC_CHECK_FUNCS): Also check for readlink. + * config.h.in: Regenerated using autoheader 2.10. + +Fri Nov 22 16:30:27 1996 Brendan Kehoe <brendan@cygnus.com> + + * configure.in: Check for -lsocket, etc., before checking for + Kerberos libraries. + * configure: Rebuild. + +1996-11-19 Jim Kingdon + + * cvsnt.mak: Remove strippath.c. + +Sun Nov 3 21:54:18 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README: Move detailed information on compatibility to + the manual; simply point to it here. + +Thu Oct 31 07:20:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * BUGS: Add note about cvs import of binary files on non-unix. + +Tue Oct 29 13:59:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * BUGS: Add note about "dying gasps" message. + +Sat Oct 26 16:17:09 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * configure.in (AC_CHECK_FUNCS): Check for tzset. + +Fri Oct 25 10:27:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Mention new loginfo features. + +Thu Oct 24 08:21:48 1996 Lars.Henriksen@netman.dk + + * INSTALL: Update to "DEC Alpha running OSF/1 version 3.2 (1.9)" + +Tue Oct 22 10:34:21 1996 Noel Cragg <noel@gargle.rain.org> + + * configure.in: don't check for the existence of the /etc/security + directory, because it's possible to have PAM installed without + using shadow passwords. + * configure: regenerated. + +Sat Oct 19 18:34:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README: Say that the remote protocol is not interoperable before + CVS 1.5. + +Sat Oct 19 13:06:53 1996 Mark H. Wilkinson <mhw@minster.york.ac.uk> + and Jim Kingdon <kingdon@cyclic.com> + + * configure.in, INSTALL: New options for configure to enable or + disable client and server code, overriding configure's defaults. + * confiugre: Regenerated. + +Sat Oct 19 13:06:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: Add note about what to do if you got a binary + distribution of CVS. Add VAX/VMS entry. + +Thu Oct 17 15:38:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS, README: Reinstate 30 Sep 96 changes concerning US letter + vs. A4 paper. + +Wed Oct 16 16:59:57 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * configure.in: Simplify code to check for crypt. Check for + -lcrypt first, and then check for the crypt function. The old + code did slightly funky things with cache variables, which JimK's + last change disturbed. Let's just keep it simple. + * configure: Rebuilt. + +Wed Oct 16 15:01:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * configure.in: Don't call unset. It isn't portable to Ultrix, + but perhaps more to the point, seems like we should be using the + cached values (there was no comment explaining why we should + ignore the cached values, and none of the CVS developers were + able to provide an explanation when I asked). + * configure: Regenerated. + + * NEWS: Add item regarding export and "cvs history". + +Tue Oct 15 07:40:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TESTS: Mention the fact that expr is only for the tests, not for + CVS itself. At least one person was unclear on this. + +Mon Oct 14 12:13:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * HACKING: Add "Submitting patches (strategy)" section and + sentence about test cases. These changes have been run by + devel-cvs and there was no objection. + +Sat Oct 12 19:43:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README.VMS: Add notes about some build problems on VAX/VMS. + +Thu Oct 10 09:20:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * BUGS: Remove item about & in modules file and client/server; the + bug is fixed. + + * README.VMS: Rewrite sections about wildcard expansion and + calling editors to suggest technical approaches and to make it + clear that fixes will only happen if someone gets around to them. + +Sat Oct 5 15:01:22 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * Version 1.9 released. + +Tue Oct 1 14:32:44 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS, README: Revert changes regarding -D, -g, and A4. They + are for new features which are not appropriate at this stage of + the release process. + +Mon Sep 30 14:51:36 1996 Greg A. Woods <woods@most.weird.com> + + * INSTALL (sun3): 1.8.86+ builds and runs make check. + + * NEWS: describe -D and -g; DIFFBIN and GREPBIN + + * MINOR-BUGS: yet another couple of annoyances... + +Mon Sep 30 08:33:51 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * BUGS: Mention "cvs add -m" client/server bug. + + * NEWS: Document change from A4 to US letter. It may seem minor, + but it affects a *lot* of people. + + * README: Revise discussion of US letter vs. A4 to reflect recent + change to cvs.texinfo. + +Sun Sep 29 16:32:47 1996 Greg A. Woods <woods@most.weird.com> + + * MINOR-BUGS: describe a minor annoyance or two + + * BUGS: describe a couple of new bugs + +Sun Sep 29 14:09:49 1996 Noel Cragg <noel@gargle.rain.org> + + * configure.in: check for shadow password files as well as for + getspnam. Some systems (like Linux) have getspnam in the C + library, but aren't necessarily using shadow passwords. + * configure, config.h.in: Regenerate. + +Fri Sep 27 16:49:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (TSUBDIRS): Remove comment about order of + directories mattering. That was only for an old set of hacks, + since gone, which tried to combine several tag files into one + (before emacs could use several tag files at once). + +Wed Sep 25 10:35:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Add note about "cvs log -d" date formats changing. See + comment I added to cvs.texinfo for more whining about this situation. + + * BUGS: Remove item about ~/.cvsignore on NT; it is fixed. + +Wed Sep 25 10:22:00 1996 Larry Jones <larry.jones@sdrc.com> + + * configure.in: Add hack for ISC crypt (the version in the posix C + library doesn't work -- why am I not surprised). Add check for + libsec.a for shadow password functions. + + * Makefile.in: Make zlib along with lib in the check targets. + +Wed Sep 25 08:34:01 1996 Jim Blandy <jimb@floss.cyclic.com> + + Fix from Mark A. Solinski <markso@mcs.com>: + * cvsnt.mak: The debug configuration adds the zlib directory to + the include path but it is missing from the release configuration. + Add it to the "ADD CPP" and "CPP_PROJ" lines. + +Tue Sep 24 11:32:20 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: Add VMS entry. Clarify what "tested" means. + + * README: Replace section about what CVS is with the blurb from + cvs.spec (which is also the paragraph we use in the release + announcements). + Change location of pcl-cvs from contrib/pcl-cvs to tools/pcl-cvs. + + * BUGS: Remove item about version numbers; we now have version + numbers. Remove item about server using /usr/tmp; this has been + changed. Remove item about deadlocks between server and client + and file contents being interpreted as commands; I believe this + refers to the case which was fixed by Ian's 7 Aug 96 change to + receive_partial_file. Remove item about server temp directory + becoming full; I'm not sure all bugs related to that have been + fixed, but I think the ones mentioned have been. Remove item + about .# files; this is a documented behavior. Refer to + platform-specific documentation. Add bug with & in modules file + and client/server CVS. Move bug about weird use of long file + names to end; the bug report is so long people won't want to read + past it. Refer to README concerning reporting bugs. Add + introduction. Reword some bug descriptions. Add bug concerning + ~/.cvsignore on NT. + * MINOR-BUGS: Add introduction. Reword some bug descriptions. + Remove item about "premature end of file"--we've improved that + error message as much as we can figure out how. Remove item about + filenames getting truncated (with rcs2log?)--I think this is a fixed + bug although I couldn't quickly find a ChangeLog entry for the fix. + +Tue Sep 17 12:46:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * .cvsignore: Add cvs-*.spec. + +Mon Sep 16 17:42:30 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TODO: In 180, mention issue of network being down. Add item + 182, about inclusiveness of "cvs log -r foo -r bar". + + * HACKING: Also mention arbitrary limits and reentrancy. + User-visible changes should be documented in cvs.texinfo as well + as NEWS. + +Thu Sep 12 16:06:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README.VMS: Put authorship info at end. Add disclaimer. Say + that patch is mandatory not optional. Don't mention gzip; we + don't require it any more. Remove section on filename case; the + bugs described there are fixed. Miscellaneous tweaks and updates. + +Wed Sep 11 11:08:39 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * configure.in (AC_OUTPUT): Don't forget to create vms/Makefile. + +Tue Sep 10 19:55:07 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (DISTFILES): Add build.com and README.VMS. + (SUBDIRS): Add vms. + * build.com: Also recurse into zlib directory. + + * NEWS: Mention Win95. + +Fri Sep 6 11:43:26 1996 Ian Lance Taylor <ian@cygnus.com> + + * configure.in: Add AC_ARG_ENABLE for encryption. + * acconfig.h: Add ENCRYPTION. + * configure, config.h.in: Regenerate. + * NEWS: Modify the entry on encryption to mention that you must + configure with --enable-encryption. + * INSTALL: Mention the --with-krb4 and --enable-encryption + configure options. + +Thu Sep 5 11:30:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Revise access method item to mention both :ext: and + :server:. + + * README.VMS: Change bug reporting address to bug-cvs. In + discussing filenames, don't mention a hypothetical behavior + involving folding to lowercase (I'm not sure what is meant, and it + doesn't sound right to me) and do mention that things might be + different now (as a result of recent changes to case sensitivity + code). + +Wed Sep 4 1996 Jim Kingdon <kingdon@cyclic.com> + + * cvsnt.mak: Add windows-NT/ChangeLog. + +Wed Sep 4 13:55:11 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (DISTFILES): Add cvs.spec. + +Mon Aug 26 15:30:13 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TODO: Add item suggesting "cvs message" command. + +Tue Aug 20 12:22:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * configure.in (AC_C_INLINE): Removed; see src/ChangeLog. + * config.h.in, configure: Regenerated. + * os2/config.h, windows-NT/config.h: Remove #define of inline. + + * configure.in (AC_C_CHAR_UNSIGNED): Removed; it is not used + anywhere. + * config.h.in, configure: Regenerated. + * os2/config.h, vms/config.h, windows-NT/config.h: Likewise, + remove __CHAR_UNSIGNED__. + +Fri Aug 16 13:37:19 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.spec (%description): Replace description with one that + resembles the release announcements we have been sending out. The + previous one was out of date and not really focused on describing + what CVS does. + (%build): Don't define SERVER_FLOWCONTROL; if we are ready to make + this is the default it should be for all kinds of builds, not just + those via RPM. + +Fri Aug 16 16:09:59 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * cvs.spec: new file. This is a template for a RPM specification + file (which is used by 'make spec'). + + * Makefile.in (installdirs-local): new (empty) target + (all install uninstall installdirs): add installdirs to list of + targets which are done for all subdirs + (spec): new target to create a rpm specification file (which can + be used to create RPM source and binary packages) + (dist): depend on spec (which now also creates .fname) + +Wed Aug 14 13:59:11 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * configure.in (AC_REPLACE_FUNCS): add getspnam for reading shadow + password entries + * configure: regenerated + * config.h.in: regenerated + +Mon Aug 12 14:15:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (config.status): When running config.status + --recheck, preserve the value of CFLAGS. + +Fri Aug 9 14:11:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TESTS: Also mention dejagnu advantages. + +Thu Aug 8 16:00:55 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TESTS (ABOUT STDOUT AND STDERR): New section. + (ABOUT TEST FRAMEWORKS): Add sed/cmp/diff (a la C News) as an option. + + * NEWS: Change entry regarding "cvs log" not invoking "rlog" so + that it emphasizes user-visible behaviors. + +Tue Aug 6 17:01:23 1996 Ian Lance Taylor <ian@cygnus.com> + + * TODO: Remove item #167 (cvs log doesn't understand symbolic + branch names). It works now. + + * NEWS: Mention that "cvs log" no longer invokes "rlog". + +Wed Jul 31 16:06:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * HACKING: Mention rule about _ vs - in file names. + +Wed Jul 24 19:10:38 1996 Ian Lance Taylor <ian@cygnus.com> + + * NEWS: Mention that Kerberos encryption is now supported. + +Mon Jul 22 23:48:39 1996 Ian Lance Taylor <ian@cygnus.com> + + * NEWS: Mention that the commit message has changed slightly when + committing changes on a branch. + +Fri Jul 19 16:10:04 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TESTS: Say that GNU expr is part of sh-utils. + +Thu Jul 18 18:16:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Mention -k wrappers option. + + * TESTS: In list of what we would like in a test framework, only + mention portable once, and other wording cleanups. + +Mon Jul 15 1996 Jim Kingdon <kingdon@cyclic.com> + + * cvsnt.mak: Add src/ChangeLog (lets us edit it from within + the integrated development environment). + +Sun Jul 14 1996 Jim Kingdon <kingdon@cyclic.com> + + * cvsnt.mak: Add src/zlib.c. Add zlib group containing the .c + files in zlib. Add /I "zlib" compiler options. + +Sun Jul 14 10:26:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Expand zlib item to emphasize user-visible (and + CVS-installer-visible) consequences. + +Sat Jul 13 21:11:50 1996 Ian Lance Taylor <ian@cygnus.com> + + * NEWS: Mention that -z now uses zlib. + +Fri Jul 12 18:54:21 1996 Ian Lance Taylor <ian@cygnus.com> + + * Makefile.in (USOURCE_SUBDIRS): Add zlib. + * configure.in (AC_OUTPUT): Add zlib/Makefile. + * configure: Regenerate. + + * zlib/*: Import zlib 1.0.3. Remove zlib/Makefile. Modify + zlib/Makefile.in for use with CVS. + +Fri Jul 12 1996 Jim Kingdon <kingdon@cyclic.com> + + * cvsnt.mak: Add src/buffer.c + +Wed Jul 10 18:44:58 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Say that rlog is deprecated. + +Tue Jul 9 14:37:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * PROJECTS: Refer to comment in rcscmds.c regarding RCS library. + + * HACKING: Expand comments on portability. + +Sun Jul 7 23:21:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * configure.in (AC_REPLACE_FUNCS): Remove memmove; it was used by + a very old version of the CVS server for nefarious purposes and it + has been long gone. + * configure: Regenerated. + +Tue Jul 2 22:36:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TESTS: Add discussion of test frameworks. + +Fri Jun 28 20:27:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Describe "cvs diff -q" removal and new diff options. + +Thu Jun 13 17:29:30 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * TODO: Remove item #67 about having cvs import create CVS + directories; I don't think it is wise to have cvs import mess with + the directory it is working in at all. Remove item #69 about + having import edit modules--in many cases there is no need for an + entry in modules. Remove item #76 about running on top of SCCS; + we are clearly not evolving in that direction. Remove item #91 + about documenting how to import sources from SCCS or RCS; this is + now documented in cvs.texinfo. Remove item #129 about "U CFTS/"; + without more information it is impossible to know what behavior is + being discussed. Remove item #157 concerning module names in cvs + release; cvs release takes a directory name, not a module name. + Remove item #159 about checking access times; this is as likely to + be an annoyance as a help, and people who are into that can just + look at the result from "cvs update" (directly or with a script). + Remove item #164 concerning variables in *info files; it is done. + Remove item #35 (it just says "cvs admin" is cheesy, which isn't + specific enough to be useful). Rewrite #39 to be specific about + what would be nice in having branches track each other. Remove + item #46--I'm not sure what it means and if it means that one + should check in with "cci" or some such instead of "cvs ci" then + that is an installation hassle and a minimal convenience. Add + item #180. + + * config.h.in: Regenerated. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@cyclic.com> + + * configure.in: Put -L${krb_libdir} in LDFLAGS temporarily when + looking for -ldes. + * configure: Regenerated. + +Mon Jun 10 13:13:35 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Mention NT local. + +Fri Jun 7 18:02:36 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@cyclic.com> + + * NEWS: Mention new annotate options. + +Thu Jun 6 14:08:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lib/savecwd.c: Revert CVS_* patch. The include files where + CVS_* is defined were not included, and the code in question was + inside HAVE_FCHDIR which isn't defined on the Mac anyway. + + * src/filesubr.c: Revert CVS_* patch in this one file. The mac + port should have its own copy of filesubr.c instead. + +Wed Jun 05 10:03:10 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * lib/{system.h,savecwd.c}, src/{add.c,checkout.c,client.c, + commit.c,create_adm.c,diff.c,edit.c,entries.c,fileattr.c, + filesubr.c,find_names.c,history.c,ignore.c,import.c,lock.c, + login.c,logmsg.c,mkmodules.c,modules.c,myndbm.c,no_diff.c, + parseinfo.c,patch.c,rcs.c,recurse.c,release.c,remove.c,root.c, + rtag.c,server.c,tag.c,update.c,vers_ts.c,wrapper.c}: + Under non-UNIX operating systems (MS-DOS, WinNT, MacOS), many + filesystem calls take only one argument; permission is handled + very differently on those systems than in UNIX. On MacOS, + the naming scheme for volumes and subdirectories is quite + different. This patch leaves hooks in the form of CVS_ACCESS, + CVS_CHDIR, CVS_CREAT, CVS_FOPEN, CVS_MKDIR, CVS_OPEN, CVS_OPENDIR, + CVS_RENAME, CVS_RMDIR, CVS_STAT, and CVS_UNLINK to accomodate + these differences. + +Thu Jun 6 11:11:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Say "changes from 1.7 to 1.8" not "changes since 1.7". + +Wed Jun 5 1996 Jim Kingdon <kingdon@cyclic.com> + + * cvsnt.mak: Visual C++ 2.1 seems to want to reformat the line + breaks. No substantive changes, I think. + +Thu May 30 15:35:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (DISTFILES): add TESTS. + +Tue May 28 13:10:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * src/server.c: Add comment regarding out-of-order bug. + * TESTS: Explain out-of-order bug. + + * INSTALL: Remove $CVSId$. More strongly encourage people to skip + the tests if they don't have the time to look at the results. + Move most of the discussion of tests to new file TESTS, and add + some information on interpreting check.log output. + * README: In brief summary of install, don't spell out details of + "make check" or "cvs init" steps. + +Sun May 26 17:59:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Change "up-to-date" to "not locally modified"; the file + need not match the head revision it only need match some revision. + +Sun May 26 17:02:49 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * NEWS: document new option "-c" for tag + +Thu May 23 21:49:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * INSTALL: Remove footnote 10. The only kind of change suitable + for listing here is fairly easy portability stuff. + +Fri May 17 11:49:11 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Refer to cvs.texinfo and say "filesystem" not "fs". + +Thu May 16 17:13:56 1996 Noel Cragg <noel@gargle.rain.org> + + * NEWS: Mention all access methods. + +Wed May 15 23:38:15 1996 Noel Cragg <noel@gargle.rain.org> + + * NEWS: add info about access methods and document behavior change + for "cvs login." + +Mon May 13 10:37:09 1996 Greg A. Woods <woods@most.weird.com> + + * INSTALL: updated for Sun-3 SunOS 4.1.1_U1 (1.8.2) + +Fri May 10 09:39:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Document that -d overrides CVS/Root. + +Mon May 6 06:00:10 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * Version 1.8.1 + Sun May 5 17:38:21 1996 Benjamin J. Lee <benjamin@cyclic.com> Integrated changes submitted by Ian Taylor <ian@cygnus.com> @@ -46,6 +890,15 @@ Wed May 1 15:38:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> * NEWS: Remove item about reserving all-uppercase tag names. +Wed May 01 00:18:01 1996 noel <noel@BOAT_ANCHOR> + + * cvsnt.mak: remove all of those unnecessary libraries! We only + need advapi32.lib and wsock32.lib. + +Wed Apr 24 16:48:35 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * NEWS: Document that -d overrides CVS/Root. + Fri Apr 19 11:22:35 1996 Benjamin J. Lee <benjamin@cyclic.com> * Version 1.7.86 diff --git a/contrib/cvs/DEVEL-CVS b/contrib/cvs/DEVEL-CVS new file mode 100644 index 0000000..defbba5 --- /dev/null +++ b/contrib/cvs/DEVEL-CVS @@ -0,0 +1,54 @@ + CVS Development Policies + +This file, DEVEL-CVS, contains the policies by which the CVS +development group operates. Also see the HACKING file. + +---------------------------------------------------------------------- +Charter for the devel-cvs mailing list: + +The CVS Developers' List <devel-cvs@cyclic.com> exists to help people +with access to the CVS source repository co-ordinate changes, make +releases, and administer the repository. + +Everyone who has been given checkin access to the repository for the +CVS sources should read devel-cvs. Only those with checkin access may +send messages to the list. + +The devel-cvs list may be used to discuss: +- administrivia regarding the CVS source repository and release + process, and +- changes and features intended for inclusion in the official CVS + release (either source code or documentation), which someone plans + to implement, or has implemented. + +The devel-cvs list should not be used to discuss: +- changes or features to packages other than the CVS release + (e.g., related packages like tkCVS, RAD/CVS, or other groups' + distributions of CVS, like RCVS, etc.), +- changes which nobody has offered to implement, or +- the philosophy of CVS (as opposed to a specific change to CVS). +These topics should either go on info-cvs, or have a new mailing list +created for them. + +The topic restrictions in this charter do not reflect the development +group's future plans for CVS; rather, they reflect a topic +classification which the group finds helpful. + +---------------------------------------------------------------------- +Policies regarding the CVS source repository: + +By checking items into the repository, developers agree to permit +distribution of such items under the terms of the GNU Public License. + +---------------------------------------------------------------------- +Procedure for dealing with people who want to be developers: + +People who want checkin access (checkout-only access is available primarily +via snapshots, for technical reasons) are first requested to send +patches and have them reviewed by a developer. If they submit some +good ones (preferably over a period of time, to demonstrate sustained +interest), then one of the developers can ask the devel-cvs mailing +list whether it is OK to make this person a developer (after first +sending the prospective developer a copy of this file and then having +the prospective developer say they want to be a developer). If there +are no objections, an account will be created. diff --git a/contrib/cvs/FAQ b/contrib/cvs/FAQ index 450fa32..c7fcdd7 100644 --- a/contrib/cvs/FAQ +++ b/contrib/cvs/FAQ @@ -7,8 +7,5 @@ questions like "what is the latest version of CVS?" and "what about GUIs for CVS?", see the CVS web site, http://www.loria.fr/~molli/cvs-index.html. There are many web sites on Configuration Management packages (of which CVS is an example); for -example see - -http://www.yahoo.com/Computers_and_Internet/Software/Software_Engineering/Configuration_Management/index.html - -or http://www.iac.honeywell.com/Pub/Tech/CM/index.html. +example see the "Configuration Management" category at http://www.yahoo.com/ +or see http://www.iac.honeywell.com/Pub/Tech/CM/index.html. diff --git a/contrib/cvs/HACKING b/contrib/cvs/HACKING index ac4cdd1..89f7399 100644 --- a/contrib/cvs/HACKING +++ b/contrib/cvs/HACKING @@ -43,11 +43,17 @@ the person checking in such a patch should reindent it. * Portability -If it is in ANSI C and it is in SunOS4 (using /bin/cc), generally it -is OK to use it without ifdefs (for example, assert() and void * as -long as you add more casts to and from void * than ANSI requires. But -not function prototypes). Such constructs are generally portable -enough, including to NT, OS/2, VMS, etc. +The general rule for portability is that it is only worth including +portability cruft for systems on which people are actually testing and +using new CVS releases. Without testing, CVS will fail to be portable +for any number of unanticipated reasons. + +The current consequence of that general rule seems to be that if it +is in ANSI C and it is in SunOS4 (using /bin/cc), generally it is OK +to use it without ifdefs (for example, assert() and void * as long as +you add more casts to and from void * than ANSI requires. But not +function prototypes). Such constructs are generally portable enough, +including to NT, OS/2, VMS, etc. * Run-time behaviors @@ -58,6 +64,22 @@ but we want to fix that code. Of course, bad input data, a corrupt repository, bad options, etc., should always print a real error message instead. +We realize that CVS contains many arbitrary limits (such as PATH_MAX). +Do not do this in new code; we are trying to *fix* those arbitrary +limits. In particular, it should be possible to pass very long +arguments (e.g. from a WWW cgi script) to CVS without having it +overrun any buffers (which might create a security hole in the WWW +example). + +Although this is a long-term goal, it also would be nice to move CVS +in the direction of reentrancy. This reduces the size of the data +segment and will allow a multi-threaded server if that is desirable. +It is also useful to write the code so that it can be easily be made +reentrant later. For example, if you need to pass data from a +Parse_Info caller to its callproc, you need a static variable. But +use a single pointer so that when Parse_Info is fixed to pass along a +void * argument, then the code can easily use that argument. + * Coding standards in general Generally speaking the GNU coding standards are mostly used by CVS @@ -65,7 +87,44 @@ Generally speaking the GNU coding standards are mostly used by CVS and perhaps an exception or two we haven't mentioned). This is the file standards.text at the GNU FTP sites. -* Submitting patches +Filenames for .c and .h files may contain _ but should not contain - +(the latter causes Visual C++ 2.1 to create makefiles which Visual C++ +4.0 cannot use). + +* Submitting patches (strategy) + +Only some kinds of changes are suitable for inclusion in the +"official" CVS. Bugfixes, where CVS's behavior contradicts the +documentation and/or expectations that everyone agrees on, should be +OK (strategically). For features, the desirable attributes are that +the need is clear and that they fit nicely into the architecture of +CVS. + +However, if there is reason to think that a change would significantly +inconvenience any significant body of CVS users, or would be +controversial for other reasons, then the design should be re-thought. +Go back to the requirements (writing documentation might help, if you +write the documentation to explain why one would use the feature not +just what the feature does). Think about whether there is a behavior +which works in both your situation and the other situations. Make a +list of the issues (for example, submit a comment or documentation +change). Ask your coworkers, a newsgroup, or a mailing list, and see +what other people think. Distribute some experimental patches outside +the "official" CVS and see what people think. By this process, the +intention is that once-controversial changes can be refined to the +point where they are relatively uncontroversial before they are +actually checked in to the "official" CVS. Features like zlib, +encryption, and others have benefitted from this process in the past +by being mentioned in the documentation and/or discussed, before an +implementation was checked in. + +If longstanding CVS behavior, that people may be relying on, is +clearly deficient, it can be changed, but only slowly and carefully. +For example, the global -q option was introduced in CVS 1.3 but the +command -q options, which the global -q replaced, were not removed +until CVS 1.6. + +* Submitting patches (tactics) Please include a ChangeLog entry (see the GNU coding standards for information on writing one) with patches. Include a description of @@ -74,7 +133,11 @@ the code are appropriate for this, but not always)--patches should not be checked in unless there is some reason for them, and the description may be helpful if there is a better way to solve the problem. In addition to the ChangeLog entry, there should be a change -to the NEWS file in the case of a new feature. +to the NEWS file and cvs.texinfo, if the change is a user-visible +change worth mentioning. + +It is nice to have a test case (see TESTS), especially for fixes which +involve subtle behaviors or twisted sections of the code. If you solve several unrelated problems, submit a separate patch for each one. Patches should be tested before submission. Use @@ -100,3 +163,30 @@ even hinted at, really) until the feature freeze which is approximately 2 weeks before the final release (at this time test releases start appearing and are announced on info-cvs). This is intentional, to avoid a last minute rush to get new features in. + +* Mailing lists + +Anyone can add themselves to the following mailing lists: + + devel-cvs. Unless you are accepted as a CVS developer as + described in the DEVEL-CVS file, you will only be able to + read this list, not send to it. The charter of the list is + also in DEVEL-CVS. + commit-cvs. The only messages sent to this list are sent + automatically, via the CVS `loginfo' mechanism, when someone + checks something in to the master CVS repository. + test-results. The only messages sent to this list are sent + automatically, daily, by a script which runs "make check" + and "make remotecheck" on the master CVS sources. +To subscribe to devel-cvs, commit-cvs, or test-results, send +a message to "majordomo@cyclic.com" whose body consists of +"subscribe <list>", where <list> is devel-cvs, commit-cvs or +test-results. + +One other list related to CVS development is bug-cvs. This is the +list which users are requested to send bug reports to. Anyone can +subscribe; to do so send mail to bug-cvs-request@prep.ai.mit.edu. + +Other CVS discussions take place on the info-cvs mailing list +(send mail to info-cvs-request@prep.ai.mit.edu to subscribe) or on +the newsgroup comp.software.config-mgmt. diff --git a/contrib/cvs/INSTALL b/contrib/cvs/INSTALL index 4907134..5632834 100644 --- a/contrib/cvs/INSTALL +++ b/contrib/cvs/INSTALL @@ -1,45 +1,52 @@ -#ident "$CVSid$" - First, read the README file. If you're still happy... CVS has been tested on the following platforms. The most recent version of CVS reported to have been tested is indicated, but more recent versions of CVS probably will work too. Please send updates to this list to bug-cvs@prep.ai.mit.edu (doing so in the form of a diff -to this file is encouraged). +to this file is encouraged). "tested" means, at a minimum, that CVS +compiles and appears to work on simple (manual) testing. In many +cases it also means "make check" and/or "make remotecheck" passes, but +we don't try to list the platforms for which that is true. Alpha: DEC Alpha running OSF/1 version 1.3 using cc (about 1.4A2) - DEC Alpha running OSF/1 version 2.0 (1.4.90) + DEC Alpha running OSF/1 version 2.0 (1.8) DEC Alpha running OSF/1 version 2.1 (about 1.4A2) DEC Alpha running OSF/1 version 3.0 (1.5.95) (footnote 7) - DEC Alpha running OSF/1 version 3.2 (1.7+obvious patch) + DEC Alpha running OSF/1 version 3.2 (1.9) + DEC Alpha running VMS 6.2 (1.8.85 client-only) +Cray: + J90 (CVS 970215 snapshot) + T3E (CVS 970215 snapshot) HPPA: HP 9000/710 running HP-UX 8.07A using gcc (about 1.4A2) - HP 9000/715 running HP-UX 9.01 (1.6) + HPPA running HP-UX 9 (1.8) HPPA running HP-UX 10.01 (1.7) HPPA 1.1 running HP-UX A.09.03 (1.5.95) (footnote 8) HPPA 1.1 running HP-UX A.09.04 (1.7.1) - NextSTEP 3.3 (1.6.86) + HPPA 9000/735 running HP-UX A.09.05 (1.8.87) + NextSTEP 3.3 (1.7) i386 family: Solaris 2.4 using gcc (about 1.4A2) UnixWare v1.1.1 using gcc (about 1.4A2) - ISC 4.0.1 (1.5.94) - Linux (kernel 1.2.x) (1.7.1) + Unixware 2.1 (1.8.86) + ISC 4.0.1 (1.8.87) + Linux (kernel 1.2.x) (1.8.86) BSDI 2.0 (1.4.93) (footnote 5) - FreeBSD 2.0.5, i486, gcc (1.5.95) - NextSTEP 3.3 (1.6.86) - NeXTSTEP 3.3 (1.7), (footnote 10) - SCO Unix 3.2.4.2 (1.4.93) (footnote 4) - SCO OpenServer 5.0.0, "CC='cc -b elf' configure" + FreeBSD 2.1.5-stable (1.8.87) + NextSTEP 3.3 (1.7) + SCO Unix 3.2.4.2, gcc 2.7.2 (1.8.87) (footnote 4) + SCO OpenServer 5 (1.8.86) Lynx 2.3.0 080695 (1.6.86) (footnote 9) - Windows NT 3.51 (1.7.87 client-only) + Windows NT 3.51 (1.8.86 client; 1.8.3 local) + Windows NT 4.0 (1.9 client and local) + Windows 95 (1.8.86 client and local) QNX 4 (1.7 + obvious patches) - OS/2 Version 3 using IBM C/C++ Tools 2.01 (1.7.86 with patches) + OS/2 Version 3 using IBM C/C++ Tools 2.01 (1.8.86 + patches, client) m68k: - Sun 3 running SunOS 4.1.1_U1 w/ bundled K&R /usr/5bin/cc (1.6) - NextSTEP 3.3 (1.6.86) - NeXTSTEP 3.3 (1.7), (footnote 10) + Sun 3 running SunOS 4.1.1_U1 w/ bundled K&R /usr/5bin/cc (1.8.86+) + NextSTEP 3.3p1 (1.8.87) Lynx 2.3.0 062695 (1.6.86) (footnote 9) m88k: Data General AViiON running dgux 5.4R2.10 (1.5) @@ -47,23 +54,26 @@ m88k: Harris Nighthawk 5800 running CX/UX 7.1 (1.5) (footnote 6) MIPS: DECstation running Ultrix 4.2a (1.4.90) - DECstation running Ultrix 4.3 (1.6.86) + DECstation running Ultrix 4.3 (1.8.85) SGI running Irix 4.0.5H using gcc and cc (about 1.4A2) (footnote 2) - SGI running Irix 5.3 (1.7) - SGI running Irix-6 (about 1.4.90) (footnote 3) + SGI running Irix 5.3 using gcc 2.7.2 (1.8.87) + SGI running Irix-6.2 (1.9.8) Siemens-Nixdorf RM600 running SINIX-Y (1.6) PowerPC or RS/6000: IBM RS/6000 running AIX 3.1 using gcc and cc (1.6.86) - IBM RS/6000 running AIX 3.2.5 (1.7.87) + IBM RS/6000 running AIX 3.2.5 (1.8) IBM RS/6000 running AIX 4.1 using gcc and cc (about 1.4A2) (footnote 1) Lynx 2.3.1 120495 (1.6.86) (footnote 9) SPARC: - Sun SPARC running SunOS 4.1.x (1.6.86) + Sun SPARC running SunOS 4.1.x (1.8.87) Sun SPARCstation 10 running Solaris 2.3 using gcc and cc (about 1.4A2) Sun SPARCstation running Solaris 2.4 using gcc and cc (about 1.5.91) - Sun SPARC running Solaris 2.5 (2.5 beta?) (1.6.4) - NextSTEP 3.3 (1.6.86) - NeXTSTEP 3.3 (1.7), (footnote 10) + Sun SPARC running Solaris 2.5 (1.8.87) + NextSTEP 3.3 (1.7) + Sun SparcClassing running Linux 2.0.17, gcc 2.7.2 (1.8.87) +VAX: + VAX running VMS 6.2 (1.9+patches, client-only) + (see README.VMS for information on necessary hacks). (footnote 1) AIX 4.1 systems fail to run "configure" due to bugs in their @@ -76,14 +86,13 @@ SPARC: workaround this bug by linking with "-lmalloc" if necessary. (about 1.4A2). -(footnote 3) - There are some warnings about pointer casts which can safely be - ignored. (about 1.4.90). - (footnote 4) Comment out the include of sys/time.h in src/server.c. (1.4.93) You also may have to make sure TIME_WITH_SYS_TIME is undef'ed. (footnote 5) Change /usr/tmp to /var/tmp in src/server.c (2 places) (1.4.93). + (This should no longer be needed; CVS doesn't have /usr/tmp in + src/server.c any more. Has anyone tried a more recent version + on BSDI? If so, please report it so we can update this file). (footnote 6) Build in ucb universe with COFF compiler tools. Put /usr/local/bin first in PATH while doing a configure, make @@ -108,15 +117,11 @@ SPARC: So after running configure I had to undef HAVE_DIRENT_H and define HAVE_SYS_DIR_H. -(footnote 10) Ralf E. Stranzenbach <ralf@reswi.ruhr.de> - I've made some modifications to "filesubr.c" to deal with NFS - mounted directories (and those funny .nfs* files). This patch - should be used whenever the programmers "sandbox" is located on - a NFS mounted device --- at least on NeXTSTEP. - ------------------------------------------------------------------------------- -Installation under Unix: +Installation under Unix (if you got a binary distribution from +somewhere, install it according to procedure for that binary +distribution, then skip to step 5): 1) Run "configure": @@ -131,18 +136,35 @@ Installation under Unix: value is "/usr/local", with binaries in sub-directory "bin", manual pages in sub-directory "man", and libraries in sub-directory "lib". + A normal build of CVS will create an executable which supports + local, server, or client CVS (if you don't know the difference, + it is described in the Repository chapter of doc/cvs.texinfo). If + you do not intend to use client or server CVS, you may want to + prevent these features from being included in the executable you + build. You can do this with the --disable-client and + --disable-server options: + + $ ./configure --disable-client --disable-server + + Typically this can reduce the size of the executable by around 30%. + If you are using server or local CVS, RCS needs to be installed in the user's PATH (or a path you have configured in src/options.h, or a path specified with the -b option). If you don't have RCS, you will need to get it from GNU as well. It is best to get the version 5.7 (or later) version of RCS, available from - prep.ai.mit.edu in the file pub/gnu/rcs-5.7.tar.gz. + prep.ai.mit.edu in the file pub/gnu/rcs-5.7.tar.gz. If you do not + have RCS version 5.x (for example, if you are using the old RCS + shipped with some versions of HPUX), CVS will probably fail to work + entirely. To find out what version of RCS you have, invoke "co -V". + If it fails to print a version number, it is an old version. If you want version control of files with binary data, make sure that the RCS configure script finds GNU diff 1.15 or later and notices that diff supports the -a option. CVS itself is much less picky about which version of diff it uses, and you shouldn't need - to worry about that. + to worry about that. If you are using GNU diff 2.6 or 2.7, you may + want to know about a (subtle) bug described in doc/DIFFUTILS-2.7-BUG. NOTE: The configure program will cache the results of the previous configure execution. If you need to re-run configure from scratch, you @@ -152,6 +174,16 @@ Installation under Unix: If you are using gcc and are planning to modify CVS, you may want to configure with -Wall; see the file HACKING for details. + If you have Kerberos 4 installed, you can specify the location of + the header files and libraries using the --with-krb4=DIR option. + DIR should be a directory with subdirectories include and lib + holding the Kerberos 4 header files and libraries, respectively. + The default value is /usr/kerberos. + + If you want to enable support for encryption over Kerberos, use + the --enable-encryption option. This option is disabled by + default. + Try './configure --help' for further information on its usage. NOTE ON CVS's USE OF NDBM: @@ -190,7 +222,7 @@ Installation under Unix: END OF NOTE FOR NDBM GUNK. 2) Edit src/options.h. Appropriate things to look at may be the - invocation locations of programs like DIFF and GREP. + invocation locations of programs like DIFF. Also glance at the default values for the environment variables that CVS uses, in particular, the RCSBIN variable, which holds the path to where the RCS programs live on your system. @@ -209,27 +241,14 @@ Installation under Unix: 3a) Run the regression tests (optional). You may also wish to validate the correctness of the new binary by - running the regression tests: - - $ make check - - Note that if your /bin/sh doesn't support shell functions, you'll - have to try something like this, where "/bin/sh5" is replaced by the - pathname of a shell which handles normal shell functions: - - $ make SHELL=/bin/sh5 check - - WARNING: This test can take quite a while to run, esp. if your - disks are slow or over-loaded. - - If you receive any un-expected output from the regression tests, - it may indicate a bug in CVS (or might just indicate a problem - running the tests). If you choose to submit a bug report, - be aware that, as with all bug reports, you may or may not get a - response, and your odds might be better if you include enough information - to reproduce the bug, an analysis of what is going wrong (if you have - the time and ability to provide one), etc. The check.log file is the - first place to look. + running the regression tests. If they succeed, that is nice to + know. However, if they fail, it doesn't tell you much. Often it + will just be a problem with running the tests on your machine, + rather than a problem with CVS. Unless you will have the time to + determine which of the two it is in case of failure, you might + want to save yourself the time and just not run the tests. + + If you want to run the tests, see the file TESTS for more information. 4) Install the binaries/documentation: diff --git a/contrib/cvs/MINOR-BUGS b/contrib/cvs/MINOR-BUGS index 7b85719..ba6fb18 100644 --- a/contrib/cvs/MINOR-BUGS +++ b/contrib/cvs/MINOR-BUGS @@ -1,26 +1,37 @@ -Low-priority bugs go here. We don't have many yet -- everything is -high-priority at the moment. :-) - - -* From: Jeff Johnson <jbj@brewster.JBJ.ORG> - To: cyclic-cvs@cyclic.com - Subject: Named_Root assumes . on server - Date: Wed, 17 May 1995 11:04:53 -0400 (EDT) - - Problem: - On server, Name_Root() attempts (aggressively) to set CVSADM_Root. - If ~/CVS/Root exists (wrto rsh login), then CVSADM_Root will be - initialized from that file. The sanity check between the root - repository and the invocation will fail if the two values are not - coincidentally the same. - - Workaround: - There's a zillion ways to fix this bugture/featurelet. My current - workaround is to remove ~/CVS/Root on the server. I shall attempt - a better fix as soon as I can determine what appears politically - correct. IMHO, the CVS/Root stuff (and getenv("CVSROOT") also) is - a bit fragile and tedious in an rcmd() driven CCVS environment. - +Low-priority bugs go here. Actually, most every documented bug is +"low-priority"--in the sense that if it is documented it means noone +has gotten around to fixing it. + + +* "cvs update -ko -p -r REV file" doesn't seem to pay attention to the + '-ko', at least in client/server mode. A simple work around is to + temporarily change the db file with "cvs admin -ko file", then switch + it back to the original modes after the checkout (probably '-kkv'). + +* "cvs status" has a difference in its output between local and + client/server mode. Namely there's a tab character followed by a + ctime(3)-style date string at the end of the "Working revision:" + field. + +* commands which don't work in a local working directory should probably + ignore any CVS/Root values and revert to using CVSROOT alone. The + current use of CVS/Root can be very confusing if you forget you're in + a working directory for a remote module -- something that's very easy + to do since CVS hides the client operation very well, esp. for + commands which fail for this reason. The only clue might be the word + "server" in a message such as this: + cvs server: cannot find module `patch' - ignored + +* cvs init may gave a strange error at times: + ttyp4:<woods@clapton> $ cvs -d /local/src-CVS init + cvs [init aborted]: cannot open CVS/Root: No such file or directory + but it seemed to work just the same.... Note that at the time CVSROOT + was set to point to a CVS server using the ":server:" option. + +* If a ~/CVS/Root file exists on the server and you are using rsh to +connect to the server, CVS may loose its mind (this was reported in +May 1995 and I suspect the symptoms have changed, but I have no +particular reason to think the bug is fixed -kingdon, Sep 96). * (Jeff Johnson <jbj@jbj.org>) I tried a "cvs status -v" and received the following: @@ -34,18 +45,8 @@ high-priority at the moment. :-) ... I claim that CVS dirs should be ignored. - - -* I sometimes get this message: - - Could not look up address for your host. Permission denied. - cvs [update aborted]: premature end of file from server - - The client's response should be cleaned up. - -* In the gb-grep module, update-ChangeLog (and therefore, I assume, - rcs2log) truncates file names --- I get entries for things called - ring/lenstring.h instead of lenstring/lenstring.h. + (I don't *think* this always happens; is "-I !" getting picked up somewhere + something like that? -kingdon, Sep 96) * On remote checkout, files don't have the right time/date stamps in the CVS/Entries files. Doesn't look like the C/S protocol has any diff --git a/contrib/cvs/Makefile.in b/contrib/cvs/Makefile.in index 636a0d7..bbf197d 100644 --- a/contrib/cvs/Makefile.in +++ b/contrib/cvs/Makefile.in @@ -11,12 +11,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# $CVSid: @(#)Makefile.in 1.30 94/10/22 $ - SHELL = /bin/sh #### Start of system configuration section. #### @@ -90,33 +84,35 @@ FLAGS_TO_PASS = \ DISTFILES = \ COPYING COPYING.LIB INSTALL README TODO PROJECTS \ - BUGS MINOR-BUGS FAQ HACKING \ + BUGS MINOR-BUGS FAQ HACKING DEVEL-CVS TESTS \ + README.VMS build.com \ ChangeLog NEWS ChangeLog.zoo \ configure configure.in stamp-h.in config.h.in Makefile.in acconfig.h \ cvs-format.el mkinstalldirs install-sh \ cvsnt.mak \ - .cvsignore + .cvsignore cvs.spec ### Subdirectories to run make in for the primary targets. # Unix source subdirs, where we'll want to run lint and etags: -USOURCE_SUBDIRS = lib src +USOURCE_SUBDIRS = lib zlib src # All other subdirs: -SUBDIRS = ${USOURCE_SUBDIRS} man doc contrib tools windows-NT os2 macintosh -# Only make TAGS/tags files in these directories, in this order -# [Why in this order? If we didn't have to stick to this order, we -# could make "TSUBDIRS = ${USOURCE_SUBDIRS}". -Karl] +SUBDIRS = ${USOURCE_SUBDIRS} man doc contrib tools \ + windows-NT os2 macintosh vms +# Only make TAGS/tags files in these directories. TSUBDIRS= src lib # Set default target. all: -.PHONY: all install uninstall -all install uninstall: config.h Makefile all-local +.PHONY: all install uninstall installdirs +all install uninstall installdirs: config.h Makefile all-local @for subdir in $(SUBDIRS); do \ echo "making $@ in $$subdir"; \ ( cd $$subdir && $(MAKE) $(FLAGS_TO_PASS) $@ ) || exit 1; \ done +installdirs: installdirs-local + install: all install-local install-info .PHONY: all-local @@ -130,6 +126,10 @@ info dvi clean-info install-info: install-local: all-local @: nothing to do locally +.PHONY: installdirs-local +installdirs-local: all-local + @: nothing to do locally + .PHONY: tags tags: @for dir in $(TSUBDIRS); do echo making $@ in $$dir; cd $$dir && $(MAKE) $(FLAGS_TO_PASS) $@ || exit 1; cd ..; done @@ -187,16 +187,19 @@ saber: .PHONY: check check: cd lib ; $(MAKE) $(FLAGS_TO_PASS) + cd zlib ; $(MAKE) $(FLAGS_TO_PASS) cd src ; $(MAKE) $(FLAGS_TO_PASS) check .PHONY: remotecheck remotecheck: cd lib ; $(MAKE) $(FLAGS_TO_PASS) + cd zlib ; $(MAKE) $(FLAGS_TO_PASS) cd src ; $(MAKE) $(FLAGS_TO_PASS) remotecheck .PHONY: installcheck installcheck: cd lib ; $(MAKE) $(FLAGS_TO_PASS) + cd zlib ; $(MAKE) $(FLAGS_TO_PASS) cd src ; $(MAKE) $(FLAGS_TO_PASS) installcheck .PHONY: lint @@ -207,12 +210,7 @@ lint: GZIP=gzip --best GZIP_EXT=.gz TAR_VERBOSE= -dist: - echo > .fname \ - cvs-`sed < $(srcdir)/src/version.c \ - -e '/version_string/!d' \ - -e 's/[^0-9.]*\([0-9.]*\).*/\1/' \ - -e q` +dist: spec rm -rf `cat .fname` ${MAKE} dist-dir DISTDIR="`cat .fname`" for dir in ${SUBDIRS}; do \ @@ -222,7 +220,7 @@ dist: ); \ done tar chf${TAR_VERBOSE} - `cat .fname` | ${GZIP} > "`cat .fname`.tar${GZIP_EXT}" - rm -rf `cat .fname` .fname + rm -rf `cat .fname` .fname .version .PHONY: dist-dir dist-dir: @@ -231,12 +229,31 @@ dist-dir: ln $(srcdir)/$${i} ${DISTDIR}; \ done +.PHONY: spec +spec: + rm -f .version .fname + sed < $(srcdir)/src/version.c \ + -e '/version_string/!d' \ + -e 's/[^0-9.]*\([0-9.]*\).*/\1/' \ + -e q > .version + echo > .fname cvs-`cat .version` + rm -f `cat .fname`.spec + sed < $(top_srcdir)/cvs.spec \ + -e 's/@VERSION@/'`cat .version`'/' \ + > `cat .fname`.spec + + # For the justification of the following Makefile rules, see node # `Automatic Remaking' in GNU Autoconf documentation. Makefile: Makefile.in config.status CONFIG_FILES=$@ CONFIG_HEADERS= ./config.status + +# Use @CFLAGS@ not $(CFLAGS) because it would be confusing for "make CFLAGS=" +# to sometimes (i.e., if configure is modified) change the configured CFLAGS, +# and sometimes not. config.status: configure - ./config.status --recheck + CFLAGS="@CFLAGS@" ./config.status --recheck + # The rules to run autoconf and autoheader are commented out. This is because # when the user unpacks a tarfile, configure.in might end up newer than # configure, but the user might not have (and does not need to have) autoconf diff --git a/contrib/cvs/NEWS b/contrib/cvs/NEWS index 81d82ac..46c78f7 100644 --- a/contrib/cvs/NEWS +++ b/contrib/cvs/NEWS @@ -1,4 +1,135 @@ -Changes since 1.7: +Changes since 1.9: + +* The client no longer needs an external patch program (assuming both +the client and the server have been updated to the new version). + +* "cvs admin [options]" will now recurse. In previous versions of +CVS, it was an error and one needed to specify "cvs admin [options] ." +to recurse. This change brings admin in line with the other CVS +commands. + +* New "logout" command to remove the password for a remote cvs +repository from the cvspass file. + +* Read-only repository access is implemented for the +password-authenticated server (other access methods are just governed +by Unix file permissions, since they require login access to the +repository machine anyway). See the "Repository" section of +cvs.texinfo for details. Note that the security cautions in +cvs.texinfo ("Password authentication security"), and the requirement +that read-only users be able to create locks and write the history +file, both apply. + +* The "checkout" command now creates a CVS directory at the top level +of the new working directory, in addition to CVS directories created +within checked-out directories. + +* There is a new administrative file verifymsg which is like editinfo +but merely validates the message, rather than also getting it from the +user. It therefore works with client/server CVS or if one uses the -m +or -F options to commit. See the verifymsg section of cvs.texinfo for +details. + +* The %s format formerly accepted in loginfo has been extended to +formats such as %{sVv}, so that loginfo scripts have access to the +version numbers being changed. See the Loginfo section of cvs.texinfo +for details. + +* The postscript documentation (doc/cvs.ps) shipped with CVS is now +formatted for US letter size instead of A4. This is not because we +consider this size "better" than A4, but because we believe that the +US letter version will print better on A4 paper than the other way +around. + +* The "cvs export" command is now logged in the history file and there +is a "cvs history -x E" command to select history file entries +produced by export. + +* CVS no longer uses the CVS_PASSWORD environment variable. Storing +passwords in cleartext in an environment variable is a security risk, +especially since (on BSD variants) any user on the system can display +any process's environment using 'ps'. Users should use the 'cvs +login' command instead. + + +Changes from 1.8 to 1.9: + +* Windows NT client should now work on Windows 95 as well. + +* New option "--help-synonyms" prints a list of all recognized command +synonyms. + +* The "log" command is now implemented internally rather than via the +RCS "rlog" program. The main user-visible consequence is that +symbolic branch names now work (for example "cvs log -rbranch1"). +Also, the date formats accepted by -d have changed. They previously +had been a bewildering variety of poorly-documented date formats. Now +they are the same as the date formats accepted by the -D options to +the other CVS commands, which is also a (different) bewildering +variety of poorly-documented date formats, but at least we are +consistently bewildering :-). + +* Encryption is now supported over a Kerberos client/server +connection. The new "-x" global option requests it. You must +configure with the --enable-encryption option in order to enable +encryption. + +* The format of the CVS commit message has changed slightly when +committing changes on a branch. The tag on which the commit is +ocurring is now reported correctly in all cases. + +* New flag -k in wrappers allows you to specify the keyword expansion +mode for added files based on their name. For example, you can +specify that files whose name matches *.exe are binary by default. +See the Wrappers section of cvs.texinfo for more details. + +* Remote CVS with the "-z" option now uses the zlib library (included +with CVS) to compress all communication between the client and the +server, rather than invoking gzip on each file separately. This means +that compression is better and there is no need for an external gzip +program (except to interoperate with older version of CVS). + +* The "cvs rlog" command is deprecated and running it will print a +warning; use the synonymous "cvs log" command instead. It is +confusing for rlog to mean the same as log because some other CVS +commands are in pairs consisting of a plain command which operates on +a working directory and an "r" command which does not (diff/rdiff; +tag/rtag). + +* "cvs diff" has a bunch of new options, mostly long options. Most of +these work only if rcsdiff and diff support them, and are named the +same as the corresponding options to diff. + +* The -q and -Q command options to "cvs diff" were removed (use the +global options instead). This brings "cvs diff" into line with the +rest of the CVS commands. + +* The "annotate" command can now be used to annotate a revision other +than the head revision on the trunk (see the -r, -D, and -f options in +the annotate node of cvs.texinfo for details). + +* The "tag" command has a new option "-c" which checks that all files + are not locally modified before tagging. + +* The -d command line option now overrides the cvsroot setting stored +in the CVS/Root file in each working directory, and specifying -d will +cause CVS/Root to be updated. + +* Local (non-client/server) CVS now runs on Windows NT. See +windows-NT/README for details. + +* The CVSROOT variable specification has changed to support more +access methods. In addition to "pserver," "server" (internal rsh +client), "ext" (external rsh client), "kserver" (kerberos), and +"local" (local filesystem access) can now be specified. For more +details on each method, see cvs.texinfo (there is an index entry for +:local: and each of the other access methods). + +* The "login" command no longer prompts the user for username and +hostname, since one will have to provide that information via the `-d' +flag or by setting CVSROOT. + +Changes from 1.7 to 1.8: * New "cvs annotate" command to display the last modification for each line of a file, with the revision number, user checking in the @@ -903,5 +1034,3 @@ Mon Nov 19 23:15:11 1990 Brian Berliner (berliner at prisma.com) 1986 version of CVS and making it available to the world. Dick's version is available on uunet.uu.net in the comp.sources.unix/volume6/cvs directory. - -$CVSid: @(#)ChangeLog 1.35 94/10/22 $ diff --git a/contrib/cvs/PROJECTS b/contrib/cvs/PROJECTS index de76576..4e20f8b 100644 --- a/contrib/cvs/PROJECTS +++ b/contrib/cvs/PROJECTS @@ -51,9 +51,10 @@ are actually more appropriate for this list. CVS parses RCS files in order to determine if work needs to be done, and then RCS parses the files again when it is performing the work. This would be much faster if CVS could do whatever is necessary - by itself. + by itself. (see comment at start of rcscmds.c for a few notes on this). 1. Improved testsuite/sanity check script * Need to use a code coverage tool to determine how much the sanity script tests, and fill in the holes. + diff --git a/contrib/cvs/README b/contrib/cvs/README index 5c4c9b6..977ddcb 100644 --- a/contrib/cvs/README +++ b/contrib/cvs/README @@ -1,5 +1,3 @@ -$CVSid: @(#)README 1.32 94/10/22 $ - CVS Kit Copyright (c) 1993-1994 Brian Berliner @@ -17,37 +15,22 @@ $CVSid: @(#)README 1.32 94/10/22 $ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ------------------------------------------------------------------------------- Welcome to CVS! -Bug reports are accepted, however note that someone may or may not -feel like taking care of your bug report. Support contracts are -available from Cyclic Software (http://www.cyclic.com or -info@cyclic.com). - -To report bugs send mail to bug-cvs@prep.ai.mit.edu, or run the "cvsbug" -program and fill out the template: +If you have problems or think you have found a bug in CVS, see the +section BUGS in the CVS manual (also known as Version Management with +CVS by Per Cederqvist et al, or cvs.texinfo--see below for details). - $ cvsbug - -The "cvsbug" program is installed in the same location as the "cvs" -program. If your installation failed, you may need to run "cvsbug" -directly out of the "src" directory as "src/cvsbug.sh". This is also -the procedure for submitting suggested changes to CVS (see the file -HACKING for more details). Note that all submitted changes may be -distributed under the terms of the GNU Public License, so if you don't -like this, don't submit them. +If you are thinking of submitting changes to CVS, see the +file HACKING. Please consult the INSTALL file for information on tested configurations. If you have a comment about an already tested -configuration, or have tried CVS on a new configuration, please write -to the above address and let us know! Free software only works if we -all help out. +configuration, or have tried CVS on a new configuration, please let us +know as described in INSTALL. Free software only works if we all help +out. Finally, we cannot guarantee that this release will not completely wipe out all of your work from your system. We do some simple testing before each @@ -61,24 +44,19 @@ Thanks for your support! ------------------------------------------------------------------------------- -CVS is a freely available collection of programs that provide for software -release and revision control functions in a UNIX environment. It is -designed to work on top of the RCS distribution, V4 and later. CVS does -understand how to parse older RCS formats, but cannot do any of the fancier -features (like vendor branch support) without RCS branch support. - -Short blurb from the manual page (larger blurb is included there): - cvs is a front end to the rcs(1) revision control system - which extends the notion of revision control from a collec- - tion of files in a single directory to a hierarchical col- - lection of directories consisting of revision controlled - files. These directories and files can be combined together - to form a software release. cvs provides the functions - necessary to manage these software releases and to control - the concurrent editing of source files among multiple - software developers. - -And a whole lot more. See the doc/cvs.texinfo file for more information. +What Is CVS? + +CVS is a version control system, which allows you to keep old versions +of files (usually source code), keep a log of who, when, and why +changes occurred, etc., like RCS or SCCS. It handles multiple +developers, multiple directories, triggers to enable/log/control +various operations, and can work over a wide area network. The +following tasks are not included; they can be done in conjunction with +CVS but will tend to require some script-writing and software other +than CVS: bug-tracking, build management (that is, make and make-like +tools), and automated testing. + +And a whole lot more. See the manual for more information. ------------------------------------------------------------------------------- @@ -86,23 +64,10 @@ Notes to people upgrading from a previous release of CVS: See the NEWS file for a description of features new in this version. -The repository format is compatible going back to CVS 1.3. But see -the "Watches compatibility" section of doc/cvs.texinfo if you have -copies of CVS 1.6 or older and you want to use the optional developer -communication features. - -The working directory format is compatible going back to CVS 1.5. It -did change between CVS 1.3 and CVS 1.5. If you run CVS 1.5 or newer -on a working directory checked out with CVS 1.3, CVS will convert it, -but to go back to CVS 1.3 you need to check out a new working -directory with CVS 1.3. - -The remote protocol is interoperable going back to CVS 1.5. Using a -client or server older than 1.5 is deprecated and may fail to work at -some point in the future (1.5 was the first official release with the -remote protocol, but some older versions might still be floating -around). In many cases you need to upgrade both the client and the -server to take advantage of new features and bugfixes, however. +See the Compatibility section of the manual for information on +compatibility between CVS versions. The quick summary is that as long +as you not using the optional watch features, there are no +compatibility problems with CVS 1.5 or later. ------------------------------------------------------------------------------- @@ -112,15 +77,18 @@ Please read the INSTALL file for installation instructions. Brief summary: $ ./configure $ make - $ make check # optional, long-running, step + (run the regression tests if desired) $ make install - $ cvsinit + (create a repository if you don't already have one) The documentation is in the doc subdirectory. cvs.texinfo is the main manual; cvs.info* and cvs.ps are the info and postscript versions, respectively, generated from cvs.texinfo. The postscript version is -for A4 paper; if you want US letter size, you need to remove the line -@afourpaper from cvs.texinfo and re-generate cvs.ps using TeX. +for US letter size paper; we do this not because we consider this size +"better" than A4, but because we believe that the US letter version +will print better on A4 paper than the other way around. If you want a +version formatted for A4, add the line @afourpaper near the start of +cvs.texinfo and re-generate cvs.ps using TeX. ------------------------------------------------------------------------------- @@ -159,7 +127,7 @@ D.C., is included in the "doc" directory. Jeff Polk from BSDI <polk@bsdi.com> converted the CVS 1.2 sources into much more readable and maintainable C code. He also added a whole lot of functionality and modularity to the code in the process. -See the ChangeLog file. +See the bottom of the NEWS file (from about 1992). david d `zoo' zuhn <zoo@armadillo.com> contributed the working base code for CVS 1.4 Alpha. His work carries on from work done by K. Richard Pixley @@ -179,9 +147,9 @@ K. Richard Pixley, Cygnus Support <rich@cygnus.com> contributed many bug fixes/enhancement as well as completing early reviews of the CVS 1.3 manual pages. -Roland Pesch, then of Cygnus Support <roland@wrs.com> contributed brand new -cvs(1) and cvs(5) manual pages. We should all thank him for saving us from -my poor use of our language! +Roland Pesch, then of Cygnus Support <roland@wrs.com> contributed +brand new cvs(1) and cvs(5) manual pages. Thanks to him for saving us +from poor use of our language! Paul Sander, HaL Computer Systems, Inc. <paul@hal.com> wrote and contributed the code in lib/sighandle.c. I added support for POSIX, BSD, @@ -190,13 +158,17 @@ and non-POSIX/non-BSD systems. Jim Kingdon and others at Cygnus Support <info@cygnus.com> wrote the remote repository access code. -In addition to the above contributors, the following Beta testers deserve -special mention for their support. If I have left off your name, I -apologize. Just write to me and let me know! +There have been many, many contributions not listed here. Consult the +ChangeLog files in each directory for a more complete idea. + +In addition to the above contributors, the following Beta testers +deserve special mention for their support. This is only a partial +list; if you have helped in this way and would like to be listed, let +bug-cvs know (as described elsewhere in this file). Mark D. Baushke <mdb@cisco.com> Per Cederqvist <ceder@signum.se> - J.T. Conklin (jtc@cygnus.com> + J.T. Conklin <jtc@cygnus.com> Vince DeMarco <vdemarco@fdcsrvr.cs.mci.com> Paul Eggert <eggert@twinsun.com> Lal George <george@research.att.com> @@ -219,4 +191,4 @@ apologize. Just write to me and let me know! Many contributors have added code to the "contrib" directory. See the README file there for a list of what is available. There is also a -contributed GNU Emacs CVS-mode in contrib/pcl-cvs. +contributed GNU Emacs CVS-mode in tools/pcl-cvs. diff --git a/contrib/cvs/README.VMS b/contrib/cvs/README.VMS new file mode 100644 index 0000000..b32ed8f --- /dev/null +++ b/contrib/cvs/README.VMS @@ -0,0 +1,159 @@ + CVS port to VMS + +DISCLAIMER: This port must be considered experimental. Although +previous versions have been in use at one large site since about +October, 1995, and the port is believed to be quite usable, various +VMS-specific quirks are known and the port cannot be considered as +mature as the ports to, say, Windows NT or unix. As always, future +progress of this port will depend on volunteer and customer interest. + +This port is of the CVS client only. Or in other words, the port +implements the full set of CVS commands, but cannot access +repositories located on the local machine. The repository must live +on another machine (a Unix box) which runs a complete port of CVS. + +Most (all?) work to date has been done on OpenVMS/AXP 6.2. Other VMS +variants might work too. + +You will also need GNU patch installed on your system. Here's a list +of ftp servers which have VMS GNU resources, taken from + + ftp://prep.ai.mit.edu/pub/gnu/vms.README + + mvb.saic.com + wuarchive.wustl.edu + ftp.wku.edu + ftp.spc.edu + ftp.stacken.kth.se + +Please send bug reports to bug-cvs@prep.ai.mit.edu. + +As of CVS 1.5.something, this port passed most of the tests in +[.src]sanity.sh. I say "most" because some tests to not apply to the +CVS client. The tests were run by hand because the VMS POSIX shell +was incapable of running the script. The tests that sanity.sh +provides are not conclusive but at least provides some assurance that +the client is usable. + +To compile, you will need DEC C (CC), DEC UCX, and of course DCL +installed on your machine. Just type "@build" in the top level +directory. This will build the sources in each subdirectory, and link +the executable [.src]cvs.exe + +Copy the executable to an appropriate directory, and define the symbol "CVS" +in a .COM file which everyone running CVS will need to run. Here's an example +of what needs to be done. + +$ CVS :== $YOUR_DEVICE:[YOUR.DIRECTORY.CVS]CVS.EXE + +Accessing a remote repository can happen in several ways. + +1. pserver +2. rsh - privileged (default) +3. rsh - unprivileged (on VMS side) + +Here's how to do each of the above: + +------------------------------------------------------------------------------- +1. pserver. This is the preferred way. It works just as it is +documented in the CVS manual (see the README file in the CVS +distribution for more information on the manual). + +------------------------------------------------------------------------------- +2. Using CVS internal rsh support (privileged) + +VMS's RSH is unusable for CVS's purposes (that is, the one in UCX. +Don't know about Multinet). However, there is code within CVS to +emulate RSH for purposes of contacting a CVS server "in the usual way" +via rshd. Unfortunately, this requires the VMS CVS client to be +installed with OPER privilege, by your system administrator. + +RSH uses privileged ports and trusted software/hosts to determine +which user on the client side is trying to connect. Part of this +security is due to the fact that on VMS or UNIX, a non privileged +process is not permitted to bind a socket to a privileged port. + +If rshd receives a connection on a non-privileged port, the connection is +immediately aborted. Only connections arriving from a privileged port will +be authenticated and served. The CVS client will therefore need privileges +under VMS to produce such a connection. + +*** Please note that no careful examination has been done of the security + implications of installing CVS with the OPER privilege. If some hole + exists, then by doing so, you will enable users who are already on + your system to gain unauthorized privileges *** + +------------------------------------------------------------------------------- +3. Using CVS internal rsh support (non-privileged) + +There is a workaround, but this is one case where I think the cure is worse +than the disease. If you patch an rshd to not care that the RSH originating +port is "non-privileged", the CVS VMS client will allow you to define the +logical CVS_RCMD_PORT to the port number where this patched rshd will be +listening. I leave the talk of patching rshd to the gentle reader and his/her +friendly system administrator. + +If I put an entry in my /etc/services file: + +cvs_rcmd 4381/tcp cvs_rcmd + +And add a line to /etc/inetd.conf, then restart inetd via "kill -1" + +cvs_rcmd stream tcp nowait root /usr/sbin/tcpd /usr/local/sbin/cvs_rcmd + +On the VMS side, you will have to do this: + +$ define CVS_RCMD_PORT 4381 + +Then run CVS in the "usual way". + +Note that the patched rshd will need to be invoked via inetd as root, so it can +authenticate and _become_ the intended user, the same as the regular rshd. + +***Please note that you will be installing a security hole by doing this.*** + +Please also note that this security hole is no larger than allowing a +Macintosh, PC (OS/2, NT, etc.) to have it's hostname in any .rhosts file, +as any user can create a privileged socket without authentication, under these +environments. In fact, existing ports of CVS to these environment use this +to their advantage. + +------------------------------------------------------------------------------- +Wildcard expansion is not yet implemented (i.e. CVS COMMIT *.c won't +work.) I think that expand_wild should be calling lib$findfile +(util.c in gzip is said to provide an example), but noone has gotten +around to implementing this. + +Log messages must be entered on the command line using -m or -F. You +can use -e or define the logical EDITOR to cause CVS to try other +editors (TPU.EXE or any other editor which wants DCL command parsing +will not work) if you want to test what's available on your system. I +haven't tested this, but if you install vi or emacs, chances are it +will probably work. Just make sure the .EXE files are in a directory +listed in VAXC$PATH (is this a typo for DCL$PATH? Also, will a +logical name work?). If someone gets around to implementing it, we +should probably be using the callable editors (e.g. TPU$TPU), although +of course we also need interface(s) which are not locked into any +particular editors. + +---------------------------------------- + +Notes regarding compiling on VAX/VMS 6.2 (not Alpha) (These are items +which hopefully will have cleaner solutions in the future, but here is +how to get around them for now): + +* Need to compile lib/getdate.c with vaxc instead of decc to avoid a +compiler bugcheck. Therefore one must add SYS$LIBRARY:VAXCRTL/LIBRARY +to the link. + +* In src/ignore.c, change lstat to stat. In vms/filesubr.c, change +"#ifdef S_ISLNK" to "#if 0". + +* Ignore the warnings in vms/vmsmunch.c; the system include file +declares something as an int when it should be void *. Not *our* +fault! + +Credits: + +Initial VMS port by Benjamin J. Lee <benjamin@cyclic.com>, Cyclic +Software, October 1, 1995 (Update March 1, 1996). diff --git a/contrib/cvs/TESTS b/contrib/cvs/TESTS new file mode 100644 index 0000000..39a38a9 --- /dev/null +++ b/contrib/cvs/TESTS @@ -0,0 +1,136 @@ +To run the tests: + + $ make check + +Note that if your /bin/sh doesn't support shell functions, you'll +have to try something like this, where "/bin/sh5" is replaced by the +pathname of a shell which handles normal shell functions: + + $ make SHELL=/bin/sh5 check + +WARNING: This test can take quite a while to run, esp. if your +disks are slow or over-loaded. + +The tests work in /tmp/cvs-sanity (which the tests create) by default. +If for some reason you want them to work in a different directory, you +can set the TESTDIR environment variable to the desired location +before running them. In particular, using SGI's Irix 6, the tests +will fail if TESTDIR is an XFS filesystem (which /tmp often is); +you'll want to set TESTDIR to a non-XFS filesystem. + +You will probably need GNU expr, which is part of the GNU sh-utils +package (this is just for running the tests; CVS itself doesn't use +expr). + +If there is some unexpected output, that is a failure which can be +somewhat hard to track down. Finding out which test is producing the +output is not always easy. The newer tests (that is, ones using +dotest*) will not have this problem, but there are many old tests +which have not been converted. + +If running the tests produces the output "FAIL:" followed by the name +of the test that failed, then the details on the failure are in the +file check.log. If it says "exit status is " followed by a number, +then the exit status of the command under test was not what the test +expected. If it says "** expected:" followed by a regular expression +followed by "** got:" followed by some text, then the regular +expression is the output which the test expected, and the text is the +output which the command under test actually produced. In some cases +you'll have to look closely to see how they differ. + +If output from "make remotecheck" is out of order compared to what is +expected (for example, + + a + b + cvs foo: this is a demo + +is expected and + + a + cvs foo: this is a demo + b + +is output), this is probably a well-known bug in the CVS server +(search for "out-of-order" in src/server.c for a comment explaining +the cause). It is a real pain in running the testsuite, but if you +are lucky and/or your machine is fast and/or lightly loaded, you won't +run into it. Running the tests again might succeed if the first run +failed in this manner. + +For more information on what goes in check.log, and how the tests are +run in general, you'll have to read sanity.sh. Depending on just what +you are looking for, and how familiar you are with the Bourne shell +and regular expressions, it will range from relatively straightforward +to obscure. + +If you choose to submit a bug report based on tests failing, be +aware that, as with all bug reports, you may or may not get a +response, and your odds might be better if you include enough +information to reproduce the bug, an analysis of what is going +wrong (if you have the time to provide one), etc. The check.log +file is the first place to look. + +ABOUT STDOUT AND STDERR +*********************** + +The sanity.sh test framework combines stdout and stderr and for tests +to pass requires that output appear in the given order. Some people +suggest that ordering between stdout and stderr should not be +required, or to put it another way, that the out-of-order bug referred +to above, and similar behaviors, should be considered features, or at +least tolerable. The reasoning behind the current behavior is that +having the output appear in a certain order is the correct behavior +for users using CVS interactively--that users get confused if the +order is unpredictable. + +ABOUT TEST FRAMEWORKS +********************* + +People periodically suggest using dejagnu or some other test +framework. A quick look at sanity.sh should make it clear that there +are indeed reasons to be dissatisfied with the status quo. Ideally a +replacement framework would achieve the following: + +1. Widely portable, including to a wide variety of unices, NT, Win95, +OS/2, VMS, probably DOS and Win3, etc. + +2. Nicely match extended regular expressions of unlimited length. + +3. Be freely redistributable, and if possible already the kind of +thing people might have already installed. The harder it is to get +and install the framework, the less people will run the tests. + +The various contenders are: + +* Bourne shell and GNU expr (the status quo). Falls short on #1 +(we've only tried unix and NT, although MKS might help with other DOS +mutants). #3 is pretty good (the main dependency is GNU expr which is +fairly widely available). + +* Bourne shell with a new regexp matcher we would distribute with +CVS. This means maintaining a regexp matcher and the makefiles which +go with it. Not clearly a win over Bourne shell and GNU expr. + +* Bourne shell, and use sed to remove variable portions of output, and +thus produce a form that can be compared with cmp or diff (this +sidesteps the need for a full regular expression matcher as mentioned +in #2 above). The C News tests are said to work this way. This would +appear to rely on variable portions of output having a certain syntax +and might spuriously recognize them out of context (this issue needs +more investigation; it isn't clear how big a problem it is in +practice). Same portability issues as the other choices based on the +Bourne shell. + +* Dejagnu. This is overkill; most of dejagnu is either unnecessary +(e.g. libraries for communicating with target boards) or undesirable +(e.g. the code which stats every file in sight to find the tests). On +the plus side, dejagnu is probably closer than any of the other +choices to having everything which is needed already there. + +* Write our own small framework directly in tcl and distribute with +CVS. The tests would look much like dejagnu tests, but we'd avoid the +unnecessary baggage. The only dependency would be on tcl (that is, +wish). + +* perl or python or <any other serious contenders here?> diff --git a/contrib/cvs/TODO b/contrib/cvs/TODO index ef9306c..7a9a816 100644 --- a/contrib/cvs/TODO +++ b/contrib/cvs/TODO @@ -1,38 +1,26 @@ -$CVSid: @(#)TODO 1.26 94/09/21 $ - -14. Pathname stripper, for checkout, as well as for writing the - Repository file. - [[ I have a simple one, but need to make sure to call it at all the - appropriate points ]] - (I'm not sure what this means -kingdon, Jun 1995). - 22. Catch signals for cleanup when "add"ing files. 24. Insist on a log message. - (This should be configurable via commitinfo or some new config file - -kingdon, Jun 1995). + (If done, this should be configurable via commitinfo or some new + config file -kingdon, Jun 1995). 30. Add "patch" program option to the modules database. 31. Think hard about ^C recovery. -35. Add "admin" command as an interface to "rcs". - [[ a cheesy version is there, but it should be re-done ]] - 38. Think hard about using RCS state information to allow one to checkin a new vendor release without having it be accessed until it has been integrated into the local changes. -39. Think about allowing parallel source trees that can easily track - each other. - [[ sort of solved with the automagic branch support, but I want more ]] +39. Think about a version of "cvs update -j" which remembers what from + that other branch is already merged. This has pitfalls--it could + easily lead to invisible state which could confuse users very + rapidly--but having to create a tag or some such mechanism to keep + track of what has been merged is a pain. -45. Consider enhancing the "patch" and "tag" command support in the module - database -- they seem hard to use since these commands deal directly - with the RCS ,v files. - -46. Perhaps checkout/checkin/tag/patch commands should be imbedded in the - file system directly, using special known command names? +45. Consider enhancing the "rdiff" and "tag" (rtag??) command support in + the module database -- they seem hard to use since these commands + deal directly with the RCS ,v files. 49. cvs xxx commands should be able to deal with files in other directories. I want to do a cvs add foo/bar.c. @@ -63,22 +51,12 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 66. Length of the CVS temporary files must be limited to 14 characters for System-V stupid support. As well as the length on the CVS.adm files. -67. cvs import should populate the vendor sources with CVS.adm files so - that one could use the vendor sources directly without having the check - them out. - -69. Consider enhacing import to add a module automatically to the module - database. Perhaps with a new option, or perhaps with an editor. - 72. Consider re-design of the module -o, -i, -t options to use the file system more intuitively. 73. Consider an option (in .cvsrc?) to automatically add files that are new and specified to commit. -76. Consider adding a layer of abstraction so that CVS can work with both - RCS and SCCS files. Larry says this should be #ifdef'ed. - 79. Might be nice to have some sort of interface to TFS and tagged revisions. @@ -91,9 +69,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 85. Add revision controlled symbolic links to CVS using one of the tag fields in the RCS file. -91. Better document the format of the source repository and how one might - convert their current SCCS or RCS files into CVS format. - 92. Look into this: After a bit of soul searching via dbx, I realized my sin was that I'd specified "echo" as the program to call from loginfo. The commit @@ -156,10 +131,15 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ (why? What is wrong with piping stdout to "tee"? -kingdon, Jun 1995) 119. Consider an option to have import checkout the RCS or SCCS files - if necessary. + if necessary. (this is if someone want to import something which is + in RCS or SCCS without preserving the history, but making sure they + do get the latest versions. It isn't clear to me how useful that is + -kingdon, June 1996). 122. If Name_Repository fails, it currently causes CVS to die completely. It - should instead return NULL and have the caller do something reasonable. + should instead return NULL and have the caller do something reasonable + (??? -what is reasonable? I'm not sure there is a real problem here. + -kingdon, June 1996). 123. Add a flag to import to not build vendor branches for local code. @@ -179,13 +159,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 128. When I tag a file, the message tells me that I'm tagging a directory. -129. Something strange seems to have happened here. When I check this out, - the update lines (U CFTS/...) seem to report a bogus leading CFTS - (e.g. U CFTS/Medusa_TS/...) when the later files are checked out. - - The directory structure doesn't seem to be botched, just the - messages. I don't recall seeing this before. - 130. cvs diff with no -r arguments does not need to look up the current RCS version number since it only cares about what's in the Entries file. This should make it much faster. @@ -197,11 +170,8 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 134. Make a statement about using hard NFS mounts to your source repository. Look into checking NULL fgets() returns with ferror() to - see if an error had occurred. - -135. The email CVS sends with each checkin, should include the version - number of each file it is checking in. - [[ Sort of solved by contrib/log.pl, which does a good job of this ]] + see if an error had occurred. (we should be checking for errors, quite + aside from NFS issues -kingdon, June 1996). 137. Some sites might want CVS to fsync() the RCS ,v file to protect against nasty hardware errors. There is a slight performance hit with @@ -252,6 +222,10 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ other than "rcsmerge" can be used, like Sun's filemerge or emacs's emerge.el. (but be careful in making this work client/server--it means doing the interactive merging at the end after the server is done). + (probably best is to have CVS do the non-interactive part and + tell the user about where the files are (.#foo.c.working and + .#foo.c.1.5 or whatever), so they can do the interactive part at + that point -kingdon, June 1996). 149. On Sun, 2 Feb 92 22:01:38 EST, rouilj@dl5000.bc.edu (John P. Rouillard) said: @@ -271,15 +245,30 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ A new command seems appropriate for this. The state can be saved in the CVS directory. I.e., - - % cvs msg foo.c + + % cvs message foo.c Enter log message for foo.c >> fixed an uninitialized variable >> ^D - The text is saved as CVS/foo.c,m (or some such name) and commit is - modified to append (prepend?) the text (if found) to the log message - specified at commit time. Easy enough. + The text is saved as CVS/foo.c,m (or some such name) and commit + is modified to append (prepend?) the text (if found) to the log + message specified at commit time. Easy enough. (having cvs + commit be non-interactive takes care of various issues like + whether to connect to the server before or after prompting for a + message (see comment in commit.c at call to start_server) + -kingdon, June 1996) + + I'm not sure about the part above about having commit prompt + for an overall message--part of the point is having commit + non-interactive and somehow combining messages seems like (excess?) + hair. + + Would be nice to do this so it allows users more flexibility in + specifying messages per-directory ("cvs message -l") or per-tree + ("cvs message") or per-file ("cvs message foo.c"), and fixes the + incompatibility between client/server (per-tree) and + non-client/server (per-directory). 151. Also, is there a flag I am missing that allows replacing Ulrtx_Build by Ultrix_build? I.E. I would like a tag replacement to be a one step @@ -311,10 +300,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ the various flags that are now available, or if somebody has a lot of files to put into a module. -157. The "cvs release" command does not understand about module names with - the same flexibility that the "checkout" and "rdiff" commands do. - It should, though, since it's confusing right now. - 158. If I do a recursive commit and find that the same RCS file is checked out (and modified!) in two different places within my checked-out files (but within the realm of a single "commit"), CVS will commit the @@ -322,13 +307,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ should catch this (typically unusual) case and issue an appropriate diagnostic and die. -159. On "update", when a merge is done, CVS should remember that your file - was merged into and should keep reminding you of this fact until you - actually look at the file (change its access time). Once you do this, - it should go back to being a normal, unmodified file. This way, after - a big update, you can run update again to see which files just got - merged and may need attention. - 160. The checks that the commit command does should be extended to make sure that the revision that we will lock is not already locked by someone else. Maybe it should also lock the new revision if the old @@ -343,11 +321,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ tag (like "Mon", "Tue", ...) all the time and still have it tag the real main-line code. -164. The *info files should allow multiple ocurrences of $CVSROOT and/or - other cvs variables. They probably should *not* expand environment - variables, as their behavior probably should not depend on who is - running CVS. - 165. The "import" command will create RCS files automatically, but will screw-up when trying to create long file names on short file name file systems. Perhaps import should be a bit more cautious. @@ -359,22 +332,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ - What all the tags mean in an "import" command - Tags are important; revision numbers are not -167. "cvs log" doesn't understand about CVS magic branch numbers. As such, - the command: - - cvs log -r1.63.2 - cvs log -rC2 - - where "C2" is a magic branch that resolves to 1.63.2 do not print the - same things. Sigh. - -169. We are using CVS as the configuration control for a software reuse library. - What we do is do system calls passing the needed arguments. In the next - release, it would be nice to see an option to put cvs .o files into a - archive library with an API. This enhancement would go nicely with the - notion of being able to integrate tools into a large software engineering - environment. - 170. Is there an "info" file that can be invoked when a file is checked out, or updated ? What I want to do is to advise users, particularly novices, of the state of their working source whenever they check something out, as @@ -421,3 +378,45 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 179. "cvs admin" does not log its actions with loginfo, nor does it check whether the action is allowed with commitinfo. It should. + +180. "cvs edit" should show you who is already editing the files, + probably (that is, do "cvs editors" before executing, or some + similar result). (But watch out for what happens if the network + is down!). + +182. There should be a way to show log entries corresponding to +changes from tag "foo" to tag "bar". "cvs log -rfoo -rbar" doesn't +cut it, because it is inclusive on the bar end. I'm not sure that is +ever a useful or logical behavior ("cvs diff -r foo -r bar" is not +similarly inclusive), but is compatibility an issue? + +183. "cvs status" should report on Entries.Static flag and CVS/Tag (how? +maybe a "cvs status -d" to give directory status?). There should also +be more documentation of how these get set and how/when to re-set them. + +184. Would be nice to implement the FreeBSD MD5-based password hash +algorithm in pserver. For more info see "6.1. DES, MD5, and Crypt" in +the FreeBSD Handbook, and src/lib/libcrypt/crypt.c in the FreeBSD +sources. Certainly in the context of non-unix servers this algorithm +makes more sense than the traditional unix crypt() algorithm, which +suffers from export control problems. + +185. A frequent complaint is that keyword expansion causes conflicts +when merging from one branch to another. The first step is +documenting CVS's existing features in this area--what happens with +various -k options in various places? The second step is thinking +about whether there should be some new feature and if so how it should +be designed. For example, here is one thought: + + rcs' co command needs a new -k option. The new option should expand + $Log entries without expanding $Revision entries. This would + allow cvs to use rcsmerge in such a way that joining branches into + main lines would neither generate extra collisions on revisions nor + drop log lines. + +The details of this are out of date (CVS no longer invokes "co", and +any changes in this area would be done by bypassing RCS rather than +modifying it), but even as to the general idea, I don't have a clear +idea about whether it would be good (see what I mean about the need +for better documentation? I work on CVS full-time, and even I don't +understand the state of the art on this subject). diff --git a/contrib/cvs/acconfig.h b/contrib/cvs/acconfig.h index 551a8aa..8bbda6f 100644 --- a/contrib/cvs/acconfig.h +++ b/contrib/cvs/acconfig.h @@ -10,3 +10,9 @@ /* Define if you want to use the password authenticated server. */ #undef AUTH_SERVER_SUPPORT + +/* Define if you want encryption support. */ +#undef ENCRYPTION + +/* Define if you have the connect function. */ +#undef HAVE_CONNECT diff --git a/contrib/cvs/config.h.in b/contrib/cvs/config.h.in index 9181936..c731d6d 100644 --- a/contrib/cvs/config.h.in +++ b/contrib/cvs/config.h.in @@ -7,11 +7,6 @@ #undef _ALL_SOURCE #endif -/* Define if type char is unsigned and you are not using gcc. */ -#ifndef __CHAR_UNSIGNED__ -#undef __CHAR_UNSIGNED__ -#endif - /* Define to empty if the keyword does not work. */ #undef const @@ -27,9 +22,6 @@ /* Define if utime(file, NULL) sets file's timestamp to the present. */ #undef HAVE_UTIME_NULL -/* Define as __inline if that's what the C compiler calls it. */ -#undef inline - /* Define if on MINIX. */ #undef _MINIX @@ -77,15 +69,15 @@ /* Define if you want to use the password authenticated server. */ #undef AUTH_SERVER_SUPPORT -/* The number of bytes in a int. */ -#undef SIZEOF_INT - -/* The number of bytes in a long. */ -#undef SIZEOF_LONG +/* Define if you want encryption support. */ +#undef ENCRYPTION /* Define if you have the connect function. */ #undef HAVE_CONNECT +/* Define if you have the crypt function. */ +#undef HAVE_CRYPT + /* Define if you have the fchdir function. */ #undef HAVE_FCHDIR @@ -104,17 +96,26 @@ /* Define if you have the getpagesize function. */ #undef HAVE_GETPAGESIZE +/* Define if you have the getspnam function. */ +#undef HAVE_GETSPNAM + +/* Define if you have the initgroups function. */ +#undef HAVE_INITGROUPS + /* Define if you have the krb_get_err_text function. */ #undef HAVE_KRB_GET_ERR_TEXT /* Define if you have the mkfifo function. */ #undef HAVE_MKFIFO +/* Define if you have the mktemp function. */ +#undef HAVE_MKTEMP + /* Define if you have the putenv function. */ #undef HAVE_PUTENV -/* Define if you have the setvbuf function. */ -#undef HAVE_SETVBUF +/* Define if you have the readlink function. */ +#undef HAVE_READLINK /* Define if you have the sigaction function. */ #undef HAVE_SIGACTION @@ -131,15 +132,24 @@ /* Define if you have the sigvec function. */ #undef HAVE_SIGVEC +/* Define if you have the tempnam function. */ +#undef HAVE_TEMPNAM + /* Define if you have the timezone function. */ #undef HAVE_TIMEZONE +/* Define if you have the tzset function. */ +#undef HAVE_TZSET + /* Define if you have the vfork function. */ #undef HAVE_VFORK /* Define if you have the vprintf function. */ #undef HAVE_VPRINTF +/* Define if you have the wait3 function. */ +#undef HAVE_WAIT3 + /* Define if you have the <direct.h> header file. */ #undef HAVE_DIRECT_H @@ -197,6 +207,9 @@ /* Define if you have the <utime.h> header file. */ #undef HAVE_UTIME_H +/* Define if you have the crypt library (-lcrypt). */ +#undef HAVE_LIBCRYPT + /* Define if you have the inet library (-linet). */ #undef HAVE_LIBINET @@ -206,5 +219,8 @@ /* Define if you have the nsl_s library (-lnsl_s). */ #undef HAVE_LIBNSL_S +/* Define if you have the sec library (-lsec). */ +#undef HAVE_LIBSEC + /* Define if you have the socket library (-lsocket). */ #undef HAVE_LIBSOCKET diff --git a/contrib/cvs/configure b/contrib/cvs/configure index 3025eef..d35580f 100755 --- a/contrib/cvs/configure +++ b/contrib/cvs/configure @@ -1,7 +1,7 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated automatically using autoconf version 2.9 +# Generated automatically using autoconf version 2.10 # Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. # # This configure script is free software; the Free Software Foundation @@ -13,6 +13,14 @@ ac_default_prefix=/usr/local # Any additions from configure.in: ac_help="$ac_help --with-krb4=value set default \$(KRB4) from value" +ac_help="$ac_help + --enable-encryption enable encryption support" +ac_help="$ac_help + --enable-client include code for running as a remote client (default) + --disable-client don't include remote client code" +ac_help="$ac_help + --enable-server include code for running as a server (default) + --disable-server don't include server code" # Initialize some variables set by options. # The variables have the same names as the options, with @@ -332,7 +340,7 @@ EOF verbose=yes ;; -version | --version | --versio | --versi | --vers) - echo "configure generated by autoconf version 2.9" + echo "configure generated by autoconf version 2.10" exit 0 ;; -with-* | --with-*) @@ -602,7 +610,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:606: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:614: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -655,13 +663,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext <<EOF -#line 659 "configure" +#line 667 "configure" #include "confdefs.h" #include <assert.h> Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:665: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:673: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -670,13 +678,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext <<EOF -#line 674 "configure" +#line 682 "configure" #include "confdefs.h" #include <assert.h> Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:680: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:688: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -698,7 +706,7 @@ echo "$ac_t""$CPP" 1>&6 echo $ac_n "checking for AIX""... $ac_c" 1>&6 cat > conftest.$ac_ext <<EOF -#line 702 "configure" +#line 710 "configure" #include "confdefs.h" #ifdef _AIX yes @@ -725,12 +733,12 @@ if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 729 "configure" +#line 737 "configure" #include "confdefs.h" #include <minix/config.h> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:734: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:742: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -787,6 +795,7 @@ fi if test "$ISC" = yes; then CFLAGS="$CFLAGS -D_SYSV3" +LIBS="-lcrypt $LIBS" fi if test "x$prefix" = xNONE; then @@ -836,11 +845,11 @@ else ac_cv_c_cross=yes else cat > conftest.$ac_ext <<EOF -#line 840 "configure" +#line 849 "configure" #include "confdefs.h" main(){return(0);} EOF -{ (eval echo configure:844: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } +{ (eval echo configure:853: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } if test -s conftest && (./conftest; exit) 2>/dev/null; then ac_cv_c_cross=no else @@ -859,7 +868,7 @@ if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 863 "configure" +#line 872 "configure" #include "confdefs.h" int main() { return 0; } @@ -909,7 +918,7 @@ ccp = (char const *const *) p; ; return 0; } EOF -if { (eval echo configure:913: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:922: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else @@ -928,102 +937,6 @@ EOF fi -echo $ac_n "checking whether char is unsigned""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_c_char_unsigned'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test "$GCC" = yes; then - # GCC predefines this symbol on systems where it applies. -cat > conftest.$ac_ext <<EOF -#line 939 "configure" -#include "confdefs.h" -#ifdef __CHAR_UNSIGNED__ - yes -#endif - -EOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - egrep "yes" >/dev/null 2>&1; then - rm -rf conftest* - ac_cv_c_char_unsigned=yes -else - rm -rf conftest* - ac_cv_c_char_unsigned=no -fi -rm -f conftest* - -else -if test "$cross_compiling" = yes; then - { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } -else -cat > conftest.$ac_ext <<EOF -#line 961 "configure" -#include "confdefs.h" -/* volatile prevents gcc2 from optimizing the test away on sparcs. */ -#if !defined(__STDC__) || __STDC__ != 1 -#define volatile -#endif -main() { - volatile char c = 255; exit(c < 0); -} -EOF -{ (eval echo configure:971: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } -if test -s conftest && (./conftest; exit) 2>/dev/null; then - ac_cv_c_char_unsigned=yes -else - ac_cv_c_char_unsigned=no -fi -fi -rm -fr conftest* -fi -fi - -echo "$ac_t""$ac_cv_c_char_unsigned" 1>&6 -if test $ac_cv_c_char_unsigned = yes && test "$GCC" != yes; then - cat >> confdefs.h <<\EOF -#define __CHAR_UNSIGNED__ 1 -EOF - -fi - -echo $ac_n "checking for inline""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_cv_c_inline=no -for ac_kw in inline __inline__ __inline; do - cat > conftest.$ac_ext <<EOF -#line 997 "configure" -#include "confdefs.h" - -int main() { return 0; } -int t() { -} $ac_kw foo() { -; return 0; } -EOF -if { (eval echo configure:1005: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then - rm -rf conftest* - ac_cv_c_inline=$ac_kw; break -fi -rm -f conftest* - -done - -fi - -echo "$ac_t""$ac_cv_c_inline" 1>&6 -case "$ac_cv_c_inline" in - inline | yes) ;; - no) cat >> confdefs.h <<\EOF -#define inline -EOF - ;; - *) cat >> confdefs.h <<EOF -#define inline $ac_cv_c_inline -EOF - ;; -esac - ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do @@ -1286,7 +1199,7 @@ if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1290 "configure" +#line 1203 "configure" #include "confdefs.h" #include <stdlib.h> #include <stdarg.h> @@ -1294,7 +1207,7 @@ else #include <float.h> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1298: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1211: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1309,7 +1222,7 @@ rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext <<EOF -#line 1313 "configure" +#line 1226 "configure" #include "confdefs.h" #include <string.h> EOF @@ -1327,7 +1240,7 @@ fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext <<EOF -#line 1331 "configure" +#line 1244 "configure" #include "confdefs.h" #include <stdlib.h> EOF @@ -1348,7 +1261,7 @@ if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext <<EOF -#line 1352 "configure" +#line 1265 "configure" #include "confdefs.h" #include <ctype.h> #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') @@ -1359,7 +1272,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF -{ (eval echo configure:1363: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } +{ (eval echo configure:1276: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } if test -s conftest && (./conftest; exit) 2>/dev/null; then : else @@ -1388,12 +1301,12 @@ if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1392 "configure" +#line 1305 "configure" #include "confdefs.h" #include <$ac_hdr> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1397: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1310: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1422,7 +1335,7 @@ if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1426 "configure" +#line 1339 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/wait.h> @@ -1439,7 +1352,7 @@ wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } EOF -if { (eval echo configure:1443: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1356: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_header_sys_wait_h=yes else @@ -1463,7 +1376,7 @@ if eval "test \"`echo '$''{'ac_cv_header_stat_broken'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1467 "configure" +#line 1380 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/stat.h> @@ -1518,7 +1431,7 @@ if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1522 "configure" +#line 1435 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/time.h> @@ -1528,7 +1441,7 @@ int t() { struct tm *tp; ; return 0; } EOF -if { (eval echo configure:1532: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1445: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_header_time=yes else @@ -1556,7 +1469,7 @@ if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1560 "configure" +#line 1473 "configure" #include "confdefs.h" #include <sys/types.h> #include <$ac_hdr> @@ -1565,7 +1478,7 @@ int t() { DIR *dirp = 0; ; return 0; } EOF -if { (eval echo configure:1569: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1482: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=yes" else @@ -1589,16 +1502,18 @@ done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then echo $ac_n "checking for -ldir""... $ac_c" 1>&6 -ac_lib_var=`echo dir_opendir | tr '.-/+' '___p'` +ac_lib_var=`echo dir'_'opendir | tr './+\055' '__p_'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-ldir $LIBS" cat > conftest.$ac_ext <<EOF -#line 1600 "configure" +#line 1513 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char opendir(); int main() { return 0; } @@ -1606,7 +1521,7 @@ int t() { opendir() ; return 0; } EOF -if { (eval echo configure:1610: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:1525: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1626,16 +1541,18 @@ fi else echo $ac_n "checking for -lx""... $ac_c" 1>&6 -ac_lib_var=`echo x_opendir | tr '.-/+' '___p'` +ac_lib_var=`echo x'_'opendir | tr './+\055' '__p_'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lx $LIBS" cat > conftest.$ac_ext <<EOF -#line 1637 "configure" +#line 1552 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char opendir(); int main() { return 0; } @@ -1643,7 +1560,7 @@ int t() { opendir() ; return 0; } EOF -if { (eval echo configure:1647: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:1564: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1668,7 +1585,7 @@ if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1672 "configure" +#line 1589 "configure" #include "confdefs.h" #include <sys/types.h> #include <signal.h> @@ -1686,7 +1603,7 @@ int t() { int i; ; return 0; } EOF -if { (eval echo configure:1690: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1607: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_type_signal=void else @@ -1708,7 +1625,7 @@ if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1712 "configure" +#line 1629 "configure" #include "confdefs.h" #include <sys/types.h> EOF @@ -1741,7 +1658,7 @@ if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1745 "configure" +#line 1662 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1772,7 +1689,7 @@ if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1776 "configure" +#line 1693 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1803,7 +1720,7 @@ if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1807 "configure" +#line 1724 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1829,19 +1746,21 @@ EOF fi -for ac_func in getwd mkdir rename strdup strstr dup2 strerror valloc waitpid memmove vasprintf strtoul +for ac_func in getwd mkdir rename strdup strstr dup2 strerror valloc waitpid vasprintf strtoul do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1840 "configure" +#line 1757 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ #include <assert.h> /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { return 0; } @@ -1858,7 +1777,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:1862: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:1781: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1878,19 +1797,123 @@ fi done -for ac_func in fchmod fsync ftime mkfifo putenv setvbuf vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock +for ac_func in fchmod fsync ftime mkfifo mktemp putenv vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tempnam tzset readlink wait3 +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1808 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { return 0; } +int t() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1832: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + +echo $ac_n "checking for evidence of shadow passwords""... $ac_c" 1>&6 +if test -f /etc/shadow \ + || test -f /etc/security/passwd.adjunct ; then + found="yes" + echo $ac_n "checking for -lsec""... $ac_c" 1>&6 +ac_lib_var=`echo sec'_'getspnam | tr './+\055' '__p_'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsec $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1867 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char getspnam(); + +int main() { return 0; } +int t() { +getspnam() +; return 0; } +EOF +if { (eval echo configure:1879: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo sec | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-lsec $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + + for ac_func in getspnam do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1889 "configure" +#line 1910 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ #include <assert.h> /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { return 0; } @@ -1907,7 +1930,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:1911: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:1934: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1929,17 +1952,24 @@ else fi done +else + found="no" +fi +echo "$ac_t""$found" 1>&6 + echo $ac_n "checking for re_exec""... $ac_c" 1>&6 if eval "test \"`echo '$''{'ac_cv_func_re_exec'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1938 "configure" +#line 1966 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char re_exec(); below. */ #include <assert.h> /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char re_exec(); int main() { return 0; } @@ -1956,7 +1986,7 @@ re_exec(); ; return 0; } EOF -if { (eval echo configure:1960: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:1990: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_func_re_exec=yes" else @@ -1984,7 +2014,7 @@ if test "$cross_compiling" = yes; then ac_cv_func_utime_null=no else cat > conftest.$ac_ext <<EOF -#line 1988 "configure" +#line 2018 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/stat.h> @@ -1995,7 +2025,7 @@ exit(!(stat ("conftestdata", &s) == 0 && utime("conftestdata", (long *)0) == 0 && t.st_mtime - s.st_mtime < 120)); } EOF -{ (eval echo configure:1999: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } +{ (eval echo configure:2029: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } if test -s conftest && (./conftest; exit) 2>/dev/null; then ac_cv_func_utime_null=yes else @@ -2059,7 +2089,7 @@ else ccvs_cv_sys_working_fnmatch=no else cat > conftest.$ac_ext <<EOF -#line 2063 "configure" +#line 2093 "configure" #include "confdefs.h" #include <fnmatch.h> @@ -2071,7 +2101,7 @@ main () ? 0 : 1); } EOF -{ (eval echo configure:2075: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } +{ (eval echo configure:2105: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } if test -s conftest && (./conftest; exit) 2>/dev/null; then ccvs_cv_sys_working_fnmatch=yes else @@ -2086,323 +2116,24 @@ if test $ccvs_cv_sys_working_fnmatch = no; then fi echo "$ac_t""$ccvs_cv_sys_working_fnmatch" 1>&6 -KRB4=/usr/kerberos - -# Check whether --with-krb4 or --without-krb4 was given. -if test "${with_krb4+set}" = set; then - withval="$with_krb4" - KRB4=$withval -fi -echo "default place for krb4 is $KRB4" - - -echo $ac_n "checking size of long""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test "$cross_compiling" = yes; then - { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } -else -cat > conftest.$ac_ext <<EOF -#line 2108 "configure" -#include "confdefs.h" -#include <stdio.h> -main() -{ - FILE *f=fopen("conftestval", "w"); - if (!f) exit(1); - fprintf(f, "%d\n", sizeof(long)); - exit(0); -} -EOF -{ (eval echo configure:2119: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } -if test -s conftest && (./conftest; exit) 2>/dev/null; then - ac_cv_sizeof_long=`cat conftestval` -else - ac_cv_sizeof_long=0 -fi -fi -rm -fr conftest* -fi -echo "$ac_t""$ac_cv_sizeof_long" 1>&6 -cat >> confdefs.h <<EOF -#define SIZEOF_LONG $ac_cv_sizeof_long -EOF - - -echo $ac_n "checking size of int""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test "$cross_compiling" = yes; then - { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } -else -cat > conftest.$ac_ext <<EOF -#line 2142 "configure" -#include "confdefs.h" -#include <stdio.h> -main() -{ - FILE *f=fopen("conftestval", "w"); - if (!f) exit(1); - fprintf(f, "%d\n", sizeof(int)); - exit(0); -} -EOF -{ (eval echo configure:2153: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } -if test -s conftest && (./conftest; exit) 2>/dev/null; then - ac_cv_sizeof_int=`cat conftestval` -else - ac_cv_sizeof_int=0 -fi -fi -rm -fr conftest* -fi -echo "$ac_t""$ac_cv_sizeof_int" 1>&6 -cat >> confdefs.h <<EOF -#define SIZEOF_INT $ac_cv_sizeof_int -EOF - - - -krb_h= -echo $ac_n "checking for krb.h""... $ac_c" 1>&6 -cat > conftest.$ac_ext <<EOF -#line 2172 "configure" -#include "confdefs.h" -#include <krb.h> -int main() { return 0; } -int t() { -int i; -; return 0; } -EOF -if { (eval echo configure:2180: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then - rm -rf conftest* - krb_h=yes krb_incdir= -else - rm -rf conftest* - if test "$cross_compiling" != yes && test -r $KRB4/include/krb.h; then - hold_cflags=$CFLAGS - CFLAGS="$CFLAGS -I$KRB4/include" - cat > conftest.$ac_ext <<EOF -#line 2189 "configure" -#include "confdefs.h" -#include <krb.h> -int main() { return 0; } -int t() { -int i; -; return 0; } -EOF -if { (eval echo configure:2197: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then - rm -rf conftest* - krb_h=yes krb_incdir=$KRB4/include -fi -rm -f conftest* - - CFLAGS=$hold_cflags - fi -fi -rm -f conftest* - -if test -z "$krb_h"; then - cat > conftest.$ac_ext <<EOF -#line 2210 "configure" -#include "confdefs.h" -#include <krb.h> -int main() { return 0; } -int t() { -int i; -; return 0; } -EOF -if { (eval echo configure:2218: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then - rm -rf conftest* - krb_h=yes krb_incdir= -else - rm -rf conftest* - if test "$cross_compiling" != yes && test -r $KRB4/include/kerberosIV/krb.h; then - hold_cflags=$CFLAGS - CFLAGS="$CFLAGS -I$KRB4/include/kerberosIV" - cat > conftest.$ac_ext <<EOF -#line 2227 "configure" -#include "confdefs.h" -#include <krb.h> -int main() { return 0; } -int t() { -int i; -; return 0; } -EOF -if { (eval echo configure:2235: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then - rm -rf conftest* - krb_h=yes krb_incdir=$KRB4/include/kerberosIV -fi -rm -f conftest* - - CFLAGS=$hold_cflags - fi -fi -rm -f conftest* - -fi -echo "$ac_t""$krb_h" 1>&6 - -if test -n "$krb_h"; then - krb_lib= - echo $ac_n "checking for -lkrb""... $ac_c" 1>&6 -ac_lib_var=`echo krb_printf | tr '.-/+' '___p'` -if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_save_LIBS="$LIBS" -LIBS="-lkrb $LIBS" -cat > conftest.$ac_ext <<EOF -#line 2259 "configure" -#include "confdefs.h" -/* Override any gcc2 internal prototype to avoid an error. */ -char printf(); - -int main() { return 0; } -int t() { -printf() -; return 0; } -EOF -if { (eval echo configure:2269: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=yes" -else - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=no" -fi -rm -f conftest* -LIBS="$ac_save_LIBS" - -fi -if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then - echo "$ac_t""yes" 1>&6 - krb_lib=yes krb_libdir= -else - echo "$ac_t""no" 1>&6 -if test "$cross_compiling" != yes && test -r $KRB4/lib/libkrb.a; then - krb_lib=yes krb_libdir=$KRB4/lib - fi -fi - - if test -n "$krb_lib"; then - cat >> confdefs.h <<\EOF -#define HAVE_KERBEROS 1 -EOF - - test -n "${krb_libdir}" && LIBS="${LIBS} -L${krb_libdir}" - LIBS="${LIBS} -lkrb" - echo $ac_n "checking for -ldes""... $ac_c" 1>&6 -ac_lib_var=`echo des_printf | tr '.-/+' '___p'` -if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_save_LIBS="$LIBS" -LIBS="-ldes $LIBS" -cat > conftest.$ac_ext <<EOF -#line 2305 "configure" -#include "confdefs.h" -/* Override any gcc2 internal prototype to avoid an error. */ -char printf(); - -int main() { return 0; } -int t() { -printf() -; return 0; } -EOF -if { (eval echo configure:2315: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=yes" -else - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=no" -fi -rm -f conftest* -LIBS="$ac_save_LIBS" - -fi -if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then - echo "$ac_t""yes" 1>&6 - LIBS="${LIBS} -ldes" -else - echo "$ac_t""no" 1>&6 -fi - - if test -n "$krb_incdir"; then - includeopt="${includeopt} -I$krb_incdir" - - fi - fi -fi -for ac_func in krb_get_err_text -do -echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext <<EOF -#line 2346 "configure" -#include "confdefs.h" -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func(); below. */ -#include <assert.h> -/* Override any gcc2 internal prototype to avoid an error. */ -char $ac_func(); - -int main() { return 0; } -int t() { - -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -$ac_func(); -#endif - -; return 0; } -EOF -if { (eval echo configure:2368: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then - rm -rf conftest* - eval "ac_cv_func_$ac_func=yes" -else - rm -rf conftest* - eval "ac_cv_func_$ac_func=no" -fi -rm -f conftest* - -fi -if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then - echo "$ac_t""yes" 1>&6 - ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` - cat >> confdefs.h <<EOF -#define $ac_tr_func 1 -EOF - -else - echo "$ac_t""no" 1>&6 -fi -done - # If we can't find connect, try looking in -lsocket, -lnsl, and -linet. # The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has # libsocket.so which has a bad implementation of gethostbyname (it # only looks in /etc/hosts), so we only look for -lsocket if we need # it. -unset ac_cv_func_connect echo $ac_n "checking for connect""... $ac_c" 1>&6 if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2401 "configure" +#line 2130 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char connect(); below. */ #include <assert.h> /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char connect(); int main() { return 0; } @@ -2419,7 +2150,7 @@ connect(); ; return 0; } EOF -if { (eval echo configure:2423: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2154: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_func_connect=yes" else @@ -2437,16 +2168,18 @@ else case "$LIBS" in *-lnsl*) ;; *) echo $ac_n "checking for -lnsl_s""... $ac_c" 1>&6 -ac_lib_var=`echo nsl_s_printf | tr '.-/+' '___p'` +ac_lib_var=`echo nsl_s'_'printf | tr './+\055' '__p_'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lnsl_s $LIBS" cat > conftest.$ac_ext <<EOF -#line 2448 "configure" +#line 2179 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char printf(); int main() { return 0; } @@ -2454,7 +2187,7 @@ int t() { printf() ; return 0; } EOF -if { (eval echo configure:2458: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2191: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2482,16 +2215,18 @@ esac case "$LIBS" in *-lnsl*) ;; *) echo $ac_n "checking for -lnsl""... $ac_c" 1>&6 -ac_lib_var=`echo nsl_printf | tr '.-/+' '___p'` +ac_lib_var=`echo nsl'_'printf | tr './+\055' '__p_'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lnsl $LIBS" cat > conftest.$ac_ext <<EOF -#line 2493 "configure" +#line 2226 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char printf(); int main() { return 0; } @@ -2499,7 +2234,7 @@ int t() { printf() ; return 0; } EOF -if { (eval echo configure:2503: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2238: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2527,16 +2262,18 @@ esac case "$LIBS" in *-lsocket*) ;; *) echo $ac_n "checking for -lsocket""... $ac_c" 1>&6 -ac_lib_var=`echo socket_connect | tr '.-/+' '___p'` +ac_lib_var=`echo socket'_'connect | tr './+\055' '__p_'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lsocket $LIBS" cat > conftest.$ac_ext <<EOF -#line 2538 "configure" +#line 2273 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char connect(); int main() { return 0; } @@ -2544,7 +2281,7 @@ int t() { connect() ; return 0; } EOF -if { (eval echo configure:2548: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2285: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2572,16 +2309,18 @@ esac case "$LIBS" in *-linet*) ;; *) echo $ac_n "checking for -linet""... $ac_c" 1>&6 -ac_lib_var=`echo inet_connect | tr '.-/+' '___p'` +ac_lib_var=`echo inet'_'connect | tr './+\055' '__p_'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-linet $LIBS" cat > conftest.$ac_ext <<EOF -#line 2583 "configure" +#line 2320 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char connect(); int main() { return 0; } @@ -2589,7 +2328,7 @@ int t() { connect() ; return 0; } EOF -if { (eval echo configure:2593: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2332: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2614,20 +2353,221 @@ else fi ;; esac -unset ac_cv_func_connect -for ac_func in connect +if test "$ac_cv_lib_socket_connect" = "yes" || test "$ac_cv_lib_inet_connect" = "yes"; then + ac_cv_func_connect=yes + cat >> confdefs.h <<\EOF +#define HAVE_CONNECT 1 +EOF + +fi +fi + + +KRB4=/usr/kerberos + +# Check whether --with-krb4 or --without-krb4 was given. +if test "${with_krb4+set}" = set; then + withval="$with_krb4" + KRB4=$withval +fi +echo "default place for krb4 is $KRB4" + + +krb_h= +echo $ac_n "checking for krb.h""... $ac_c" 1>&6 +cat > conftest.$ac_ext <<EOF +#line 2380 "configure" +#include "confdefs.h" +#include <krb.h> +int main() { return 0; } +int t() { +int i; +; return 0; } +EOF +if { (eval echo configure:2388: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + rm -rf conftest* + krb_h=yes krb_incdir= +else + rm -rf conftest* + if test "$cross_compiling" != yes && test -r $KRB4/include/krb.h; then + hold_cflags=$CFLAGS + CFLAGS="$CFLAGS -I$KRB4/include" + cat > conftest.$ac_ext <<EOF +#line 2397 "configure" +#include "confdefs.h" +#include <krb.h> +int main() { return 0; } +int t() { +int i; +; return 0; } +EOF +if { (eval echo configure:2405: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + rm -rf conftest* + krb_h=yes krb_incdir=$KRB4/include +fi +rm -f conftest* + + CFLAGS=$hold_cflags + fi +fi +rm -f conftest* + +if test -z "$krb_h"; then + cat > conftest.$ac_ext <<EOF +#line 2418 "configure" +#include "confdefs.h" +#include <krb.h> +int main() { return 0; } +int t() { +int i; +; return 0; } +EOF +if { (eval echo configure:2426: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + rm -rf conftest* + krb_h=yes krb_incdir= +else + rm -rf conftest* + if test "$cross_compiling" != yes && test -r $KRB4/include/kerberosIV/krb.h; then + hold_cflags=$CFLAGS + CFLAGS="$CFLAGS -I$KRB4/include/kerberosIV" + cat > conftest.$ac_ext <<EOF +#line 2435 "configure" +#include "confdefs.h" +#include <krb.h> +int main() { return 0; } +int t() { +int i; +; return 0; } +EOF +if { (eval echo configure:2443: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + rm -rf conftest* + krb_h=yes krb_incdir=$KRB4/include/kerberosIV +fi +rm -f conftest* + + CFLAGS=$hold_cflags + fi +fi +rm -f conftest* + +fi +echo "$ac_t""$krb_h" 1>&6 + +if test -n "$krb_h"; then + krb_lib= + echo $ac_n "checking for -lkrb""... $ac_c" 1>&6 +ac_lib_var=`echo krb'_'printf | tr './+\055' '__p_'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lkrb $LIBS" +cat > conftest.$ac_ext <<EOF +#line 2467 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char printf(); + +int main() { return 0; } +int t() { +printf() +; return 0; } +EOF +if { (eval echo configure:2479: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + krb_lib=yes krb_libdir= +else + echo "$ac_t""no" 1>&6 +if test "$cross_compiling" != yes && test -r $KRB4/lib/libkrb.a; then + krb_lib=yes krb_libdir=$KRB4/lib + fi +fi + + if test -n "$krb_lib"; then + cat >> confdefs.h <<\EOF +#define HAVE_KERBEROS 1 +EOF + + test -n "${krb_libdir}" && LIBS="${LIBS} -L${krb_libdir}" + LIBS="${LIBS} -lkrb" + # Put -L${krb_libdir} in LDFLAGS temporarily so that it appears before + # -ldes in the command line. Don't do it permanently so that we honor + # the user's setting for LDFLAGS + hold_ldflags=$LDFLAGS + test -n "${krb_libdir}" && LDFLAGS="$LDFLAGS -L${krb_libdir}" + echo $ac_n "checking for -ldes""... $ac_c" 1>&6 +ac_lib_var=`echo des'_'printf | tr './+\055' '__p_'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldes $LIBS" +cat > conftest.$ac_ext <<EOF +#line 2520 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char printf(); + +int main() { return 0; } +int t() { +printf() +; return 0; } +EOF +if { (eval echo configure:2532: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="${LIBS} -ldes" +else + echo "$ac_t""no" 1>&6 +fi + + LDFLAGS=$hold_ldflags + if test -n "$krb_incdir"; then + includeopt="${includeopt} -I$krb_incdir" + + fi + fi +fi +for ac_func in krb_get_err_text do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2626 "configure" +#line 2564 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ #include <assert.h> /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { return 0; } @@ -2644,7 +2584,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:2648: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2588: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -2666,20 +2606,39 @@ else fi done + +# Check whether --enable-encryption or --disable-encryption was given. +if test "${enable_encryption+set}" = set; then + enableval="$enable_encryption" + case "${enableval}" in + yes) encryption=true ;; + no) encryption=false ;; + *) { echo "configure: error: bad value ${enableval} for encryption option" 1>&2; exit 1; } ;; + esac +else + encryption=false fi +if test "$encryption" = "true"; then + cat >> confdefs.h <<\EOF +#define ENCRYPTION 1 +EOF + +fi echo $ac_n "checking for gethostname""... $ac_c" 1>&6 if eval "test \"`echo '$''{'ac_cv_func_gethostname'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2678 "configure" +#line 2635 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char gethostname(); below. */ #include <assert.h> /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char gethostname(); int main() { return 0; } @@ -2696,7 +2655,7 @@ gethostname(); ; return 0; } EOF -if { (eval echo configure:2700: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2659: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_func_gethostname=yes" else @@ -2715,73 +2674,69 @@ LIBOBJS="$LIBOBJS hostname.o" fi -# If we have connect(), we want the full client & server arrangement. -if test "$ac_cv_func_connect" = yes; then -cat >> confdefs.h <<\EOF +# Check for options requesting client and server feature. If none are +# given and we have connect(), we want the full client & server arrangement. +# Check whether --enable-client or --disable-client was given. +if test "${enable_client+set}" = set; then + enableval="$enable_client" + if test "$enable_client" = yes; then + cat >> confdefs.h <<\EOF +#define CLIENT_SUPPORT 1 +EOF + +fi +else + if test "$ac_cv_func_connect" = yes; then + cat >> confdefs.h <<\EOF #define CLIENT_SUPPORT 1 EOF -cat >> confdefs.h <<\EOF +fi +fi + +# Check whether --enable-server or --disable-server was given. +if test "${enable_server+set}" = set; then + enableval="$enable_server" + if test "$enable_server" = yes; then + cat >> confdefs.h <<\EOF #define SERVER_SUPPORT 1 EOF -# Define AUTH_SERVER_SUPPORT only if we could locate the crypt() function -unset ac_cv_func_crypt -echo $ac_n "checking for crypt""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_func_crypt'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 +fi else - cat > conftest.$ac_ext <<EOF -#line 2736 "configure" -#include "confdefs.h" -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char crypt(); below. */ -#include <assert.h> -/* Override any gcc2 internal prototype to avoid an error. */ -char crypt(); + if test "$ac_cv_func_connect" = yes; then + cat >> confdefs.h <<\EOF +#define SERVER_SUPPORT 1 +EOF -int main() { return 0; } -int t() { + enable_server=yes +fi +fi -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_crypt) || defined (__stub___crypt) -choke me -#else -crypt(); -#endif -; return 0; } -EOF -if { (eval echo configure:2758: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then - rm -rf conftest* - eval "ac_cv_func_crypt=yes" -else - rm -rf conftest* - eval "ac_cv_func_crypt=no" -fi -rm -f conftest* +### The auth server needs to be able to check passwords against passwd +### file entries, so we only #define AUTH_SERVER_SUPPORT if we can +### find the crypt function. +### +### We used to test for crypt in libc first, and only add -lcrypt if +### we couldn't find it, but that interacts badly with the cache +### variables, the 'unset' command isn't portable, and I'm not sure +### there's any harm in just testing for -lcrypt first. -fi -if eval "test \"`echo '$ac_cv_func_'crypt`\" = yes"; then - echo "$ac_t""yes" 1>&6 - : -else - echo "$ac_t""no" 1>&6 -case "$LIBS" in -*-lcrypt*) ;; -*) echo $ac_n "checking for -lcrypt""... $ac_c" 1>&6 -ac_lib_var=`echo crypt_crypt | tr '.-/+' '___p'` +if test "$enable_server" = yes; then +echo $ac_n "checking for -lcrypt""... $ac_c" 1>&6 +ac_lib_var=`echo crypt'_'crypt | tr './+\055' '__p_'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lcrypt $LIBS" cat > conftest.$ac_ext <<EOF -#line 2783 "configure" +#line 2736 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char crypt(); int main() { return 0; } @@ -2789,7 +2744,7 @@ int t() { crypt() ; return 0; } EOF -if { (eval echo configure:2793: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2748: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2812,9 +2767,7 @@ EOF else echo "$ac_t""no" 1>&6 fi - ;; -esac -unset ac_cv_func_crypt + for ac_func in crypt do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 @@ -2822,12 +2775,14 @@ if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2826 "configure" +#line 2779 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ #include <assert.h> /* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { return 0; } @@ -2844,7 +2799,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:2848: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then +if { (eval echo configure:2803: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -2866,15 +2821,14 @@ else fi done -fi - if test "$ac_cv_func_crypt" = yes; then +if test "$ac_cv_func_crypt" = yes; then cat >> confdefs.h <<\EOF #define AUTH_SERVER_SUPPORT 1 EOF - fi fi +fi # enable_server test -f src/options.h && ( echo "configure: warning: saving ./src/options.h in ./src/options.h-SAVED" 1>&2 @@ -2959,7 +2913,7 @@ do echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v) - echo "$CONFIG_STATUS generated by autoconf version 2.9" + echo "$CONFIG_STATUS generated by autoconf version 2.10" exit 0 ;; -help | --help | --hel | --he | --h) echo "\$ac_cs_usage"; exit 0 ;; @@ -2970,10 +2924,12 @@ done ac_given_srcdir=$srcdir ac_given_INSTALL="$INSTALL" -trap 'rm -fr `echo "Makefile lib/Makefile src/Makefile doc/Makefile \ +trap 'rm -fr `echo "Makefile lib/Makefile src/Makefile zlib/Makefile doc/Makefile \ man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ - windows-NT/Makefile os2/Makefile macintosh/Makefile stamp-h config.h src/options.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 + windows-NT/Makefile windows-NT/SCC/Makefile \ + os2/Makefile macintosh/Makefile vms/Makefile \ + stamp-h config.h src/options.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 EOF cat >> $CONFIG_STATUS <<EOF @@ -3021,10 +2977,12 @@ CEOF EOF cat >> $CONFIG_STATUS <<EOF -CONFIG_FILES=\${CONFIG_FILES-"Makefile lib/Makefile src/Makefile doc/Makefile \ +CONFIG_FILES=\${CONFIG_FILES-"Makefile lib/Makefile src/Makefile zlib/Makefile doc/Makefile \ man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ - windows-NT/Makefile os2/Makefile macintosh/Makefile stamp-h"} + windows-NT/Makefile windows-NT/SCC/Makefile \ + os2/Makefile macintosh/Makefile vms/Makefile \ + stamp-h"} EOF cat >> $CONFIG_STATUS <<\EOF for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then @@ -3171,6 +3129,12 @@ cat >> $CONFIG_STATUS <<\EOF echo "$ac_file is unchanged" rm -f conftest.h else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi rm -f $ac_file mv conftest.h $ac_file fi diff --git a/contrib/cvs/configure.in b/contrib/cvs/configure.in index 554a401..87f4b7d 100644 --- a/contrib/cvs/configure.in +++ b/contrib/cvs/configure.in @@ -1,7 +1,17 @@ dnl configure.in for cvs -dnl "$CVSid$" AC_INIT(src/cvs.h) +dnl AC_PREREQ(2.4)dnl Required Autoconf version. +dnl Do not use autoconf 2.12; it produces a configure script which produces +dnl a "internal 2K buffer" error on HPUX when run with /bin/sh. +dnl autoconf 2.10 seems like a good choice. +dnl +dnl It is possible that we should just change the above required version +dnl to 2.10; it seems like everyone is using 2.10 anyway, and there is +dnl at least some sentiment that we should be using a version which has +dnl --bindir (and correspondingly, using @bindir@ and friends in our +dnl Makefile.in files. I'm not sure exactly what version of autoconf +dnl introduced --bindir but I know 2.10 has it. AC_CONFIG_HEADER(config.h src/options.h) AC_PROG_CC @@ -11,15 +21,17 @@ AC_MINIX AC_ISC_POSIX if test "$ISC" = yes; then CFLAGS="$CFLAGS -D_SYSV3" +LIBS="-lcrypt $LIBS" fi AC_PREFIX_PROGRAM(cvs) +dnl FIXME: AC_C_CROSS is considered obsolete by autoconf 2.12, and is +dnl pretty ugly to start with. But it isn't obvious to me how we should +dnl be handling the uses of cross_compiling below. AC_C_CROSS AC_C_CONST -AC_C_CHAR_UNSIGNED -AC_C_INLINE AC_PROG_INSTALL AC_PROG_RANLIB @@ -49,8 +61,29 @@ AC_TYPE_UID_T AC_TYPE_MODE_T AC_TYPE_SIZE_T AC_TYPE_PID_T -AC_REPLACE_FUNCS(getwd mkdir rename strdup strstr dup2 strerror valloc waitpid memmove vasprintf strtoul) -AC_CHECK_FUNCS(fchmod fsync ftime mkfifo putenv setvbuf vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock) +AC_REPLACE_FUNCS(getwd mkdir rename strdup strstr dup2 strerror valloc waitpid vasprintf strtoul) +AC_CHECK_FUNCS(fchmod fsync ftime mkfifo mktemp putenv vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tempnam tzset readlink wait3) + +dnl +dnl Look for shadow password files before we go ahead and set getspnam. +dnl On some systems (Linux), the C library has getspnam but shadow +dnl passwords might not be in use. +dnl +dnl We used to check for the existence of the /etc/security directory +dnl here, but that's incorrect, since it's possible to have PAM installed +dnl without using shadow passwords. +dnl +AC_MSG_CHECKING([for evidence of shadow passwords]) +if test -f /etc/shadow \ + || test -f /etc/security/passwd.adjunct ; then + found="yes" + AC_CHECK_LIB(sec, getspnam) + AC_CHECK_FUNCS(getspnam) +else + found="no" +fi +AC_MSG_RESULT([$found]) + AC_CHECK_FUNC(re_exec, :, LIBOBJS="$LIBOBJS regex.o") AC_FUNC_UTIME_NULL AC_SYS_LONG_FILE_NAMES @@ -74,6 +107,35 @@ if test $ccvs_cv_sys_working_fnmatch = no; then fi AC_MSG_RESULT($ccvs_cv_sys_working_fnmatch) +# If we can't find connect, try looking in -lsocket, -lnsl, and -linet. +# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has +# libsocket.so which has a bad implementation of gethostbyname (it +# only looks in /etc/hosts), so we only look for -lsocket if we need +# it. +AC_CHECK_FUNC(connect, :, +[case "$LIBS" in +*-lnsl*) ;; +*) AC_CHECK_LIB(nsl_s, printf) ;; +esac +case "$LIBS" in +*-lnsl*) ;; +*) AC_CHECK_LIB(nsl, printf) ;; +esac +case "$LIBS" in +*-lsocket*) ;; +*) AC_CHECK_LIB(socket, connect) ;; +esac +case "$LIBS" in +*-linet*) ;; +*) AC_CHECK_LIB(inet, connect) ;; +esac +dnl We can't just call AC_CHECK_FUNCS(connect) here, because the value +dnl has been cached. +if test "$ac_cv_lib_socket_connect" = "yes" || test "$ac_cv_lib_inet_connect" = "yes"; then + ac_cv_func_connect=yes + AC_DEFINE(HAVE_CONNECT) +fi]) + dnl dnl set $(KRB4) from --with-krb4=value -- WITH_KRB4 dnl @@ -87,9 +149,6 @@ echo "default place for krb4 is $KRB4" AC_SUBST(KRB4)])dnl WITH_KRB4 -AC_CHECK_SIZEOF(long) -AC_CHECK_SIZEOF(int) - krb_h= AC_MSG_CHECKING([for krb.h]) AC_TRY_LINK([#include <krb.h>],[int i;], @@ -124,7 +183,13 @@ if test -n "$krb_h"; then AC_DEFINE(HAVE_KERBEROS) test -n "${krb_libdir}" && LIBS="${LIBS} -L${krb_libdir}" LIBS="${LIBS} -lkrb" + # Put -L${krb_libdir} in LDFLAGS temporarily so that it appears before + # -ldes in the command line. Don't do it permanently so that we honor + # the user's setting for LDFLAGS + hold_ldflags=$LDFLAGS + test -n "${krb_libdir}" && LDFLAGS="$LDFLAGS -L${krb_libdir}" AC_CHECK_LIB(des,printf,[LIBS="${LIBS} -ldes"]) + LDFLAGS=$hold_ldflags if test -n "$krb_incdir"; then includeopt="${includeopt} -I$krb_incdir" AC_SUBST(includeopt) @@ -132,51 +197,63 @@ if test -n "$krb_h"; then fi fi AC_CHECK_FUNCS(krb_get_err_text) -# If we can't find connect, try looking in -lsocket, -lnsl, and -linet. -# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has -# libsocket.so which has a bad implementation of gethostbyname (it -# only looks in /etc/hosts), so we only look for -lsocket if we need -# it. -unset ac_cv_func_connect -AC_CHECK_FUNC(connect, :, -[case "$LIBS" in -*-lnsl*) ;; -*) AC_CHECK_LIB(nsl_s, printf) ;; -esac -case "$LIBS" in -*-lnsl*) ;; -*) AC_CHECK_LIB(nsl, printf) ;; -esac -case "$LIBS" in -*-lsocket*) ;; -*) AC_CHECK_LIB(socket, connect) ;; -esac -case "$LIBS" in -*-linet*) ;; -*) AC_CHECK_LIB(inet, connect) ;; -esac -unset ac_cv_func_connect -AC_CHECK_FUNCS(connect)]) + +dnl +dnl Use --with-encryption to turn on encryption support +dnl +AC_ARG_ENABLE(encryption, + [ --enable-encryption enable encryption support], + [case "${enableval}" in + yes) encryption=true ;; + no) encryption=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for encryption option) ;; + esac], + [encryption=false]) +if test "$encryption" = "true"; then + AC_DEFINE(ENCRYPTION) +fi AC_CHECK_FUNC(gethostname, :, LIBOBJS="$LIBOBJS hostname.o") -# If we have connect(), we want the full client & server arrangement. -if test "$ac_cv_func_connect" = yes; then -AC_DEFINE(CLIENT_SUPPORT) -AC_DEFINE(SERVER_SUPPORT) -# Define AUTH_SERVER_SUPPORT only if we could locate the crypt() function -unset ac_cv_func_crypt -AC_CHECK_FUNC(crypt, :, -[case "$LIBS" in -*-lcrypt*) ;; -*) AC_CHECK_LIB(crypt, crypt) ;; -esac -unset ac_cv_func_crypt -AC_CHECK_FUNCS(crypt)]) - if test "$ac_cv_func_crypt" = yes; then +# Check for options requesting client and server feature. If none are +# given and we have connect(), we want the full client & server arrangement. +AC_ARG_ENABLE(client, +[ --enable-client include code for running as a remote client (default) + --disable-client don't include remote client code], +[if test "$enable_client" = yes; then + AC_DEFINE(CLIENT_SUPPORT) +fi], +[if test "$ac_cv_func_connect" = yes; then + AC_DEFINE(CLIENT_SUPPORT) +fi]) +AC_ARG_ENABLE(server, +[ --enable-server include code for running as a server (default) + --disable-server don't include server code], +[if test "$enable_server" = yes; then + AC_DEFINE(SERVER_SUPPORT) +fi], +[if test "$ac_cv_func_connect" = yes; then + AC_DEFINE(SERVER_SUPPORT) + enable_server=yes +fi]) + +### The auth server needs to be able to check passwords against passwd +### file entries, so we only #define AUTH_SERVER_SUPPORT if we can +### find the crypt function. +### +### We used to test for crypt in libc first, and only add -lcrypt if +### we couldn't find it, but that interacts badly with the cache +### variables, the 'unset' command isn't portable, and I'm not sure +### there's any harm in just testing for -lcrypt first. + +if test "$enable_server" = yes; then +AC_CHECK_LIB(crypt, crypt) +AC_CHECK_FUNCS(crypt) + +if test "$ac_cv_func_crypt" = yes; then AC_DEFINE(AUTH_SERVER_SUPPORT) - fi fi +fi # enable_server test -f src/options.h && ( AC_MSG_WARN(saving ./src/options.h in ./src/options.h-SAVED) @@ -185,7 +262,9 @@ test -f src/options.h && ( cp ./src/options.h ./src/options.h-SAVED ) -AC_OUTPUT(Makefile lib/Makefile src/Makefile doc/Makefile \ +AC_OUTPUT(Makefile lib/Makefile src/Makefile zlib/Makefile doc/Makefile \ man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ - windows-NT/Makefile os2/Makefile macintosh/Makefile stamp-h) + windows-NT/Makefile windows-NT/SCC/Makefile \ + os2/Makefile macintosh/Makefile vms/Makefile \ + stamp-h) diff --git a/contrib/cvs/contrib/ChangeLog b/contrib/cvs/contrib/ChangeLog index 80db5b8..3fbee61 100644 --- a/contrib/cvs/contrib/ChangeLog +++ b/contrib/cvs/contrib/ChangeLog @@ -1,3 +1,121 @@ +Mon May 12 11:59:23 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * listener.c: Removed; see ../ChangeLog for rationale. + +10 May 1997 Jim Kingdon + + * listen2.c, listen2.mak: New files. + * Makefile.in (DISTFILES): Add them. + * .cvsignore: Add Debug. + +Thu Feb 20 22:43:45 1997 David J MacKenzie <djm@va.pubnix.com> + + * rcs-to-cvs.sh: Put temporary files in /var/tmp or /usr/tmp + whichever one exists. Just call "vi" not "/usr/ucb/vi". + +Mon Feb 17 08:51:37 1997 Greg A. Woods <woods@most.weird.com> + + * .cvsignore: added 'cvs2vendor' target from Feb. 12 changes. + + * log_accum.pl (build_header): added "Repository:" to the report + header to show the first argument supplied to the script by CVS. + [[this value seems spuriously to be wrong when client is used]] + ($hostdomain): correct order of initialization from the Feb. 12 + changes. + ($modulename): add more commentary about using '-M' to to get a + meaningful string here. + Tweak a few other comments from the Feb. 12 changes. + +Wed Feb 12 10:27:48 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cln_hist.pl, commit_prep.pl, cvs2vendor.sh, cvs_acls.pl, + cvscheck.man, cvscheck.sh, cvshelp.man, descend.man, descend.sh, + log_accum.pl, mfpipe.pl, rcs-to-cvs.sh, rcs2log.sh, rcs2sccs.sh, + sccs2rcs.csh: Remove $Id; we decided to get rid of these some + time ago. + +Wed Feb 12 00:24:33 1997 Greg A. Woods <woods@most.weird.com> + + * cvs2vendor.sh: new script. + * README: noted new cvs2vendor script. + * Makefile.in (DISTFILES): added cvs2vendor.sh. + (CONTRIB_PROGS): added cvs2vendor. + + * log_accum.pl (show_wd): new variable, initialized to 0. + - set $show_wd if '-w' option found while parsing @ARGV. + - don't add 'In directory' line to report header unless $show_wd + is set. + (domainname): prepend a leading '.' if none there so that + concatenation with $hostname works (those with a FQDN hostname + *and* a domainname still lose). + (mail_notification): don't set a "From:" header -- the mailer will. + +Wed Jan 8 14:48:58 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in, README, log.pl: Remove CVSid; we decided to get rid + of these some time ago. + +Thu Jan 2 13:30:56 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in: Remove "675" paragraph; see ../ChangeLog for rationale. + +Thu Oct 17 18:28:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * patch-2.1-.new-fix: Removed; it was not in DISTFILES so it never + made it into distributions. It also isn't clear what it has to do + with CVS. It is available from + ftp://ftp.weird.com/pub/patch-2.1-.new-fix + * README: Remove entry for patch-2.1-.new-fix. + +Wed Oct 16 10:22:44 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * rcs2log.sh: Change date output format to something CVS 1.9 + accepts. I think this breaks the Sep 29 change, but I don't have + a copy of CVS 1.5 handy, so I can't find a format that works with + both, and I think it's more important that it work with the + version it's distributed with. + +Sat Oct 12 21:18:19 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README: Don't mention pcl-cvs; it isn't here any more. + +Sun Sep 29 19:45:19 1996 Greg A. Woods <woods@most.weird.com> + + * README: add entry for patch-2.1-.new-fix. + + * README: re-write the top section a bit. + + * patch-2.1-.new-fix: re-generated using fixed "cvs patch" command. + + * patch-2.1-.new-fix: new file. + +Sun Sep 29 14:25:28 1996 Dave Love <d.love@dl.ac.uk> + + * rcs2log.sh (month_data): Make default date format acceptable to + CVS post v1.8 as well as earlier CVSs and RCS. + Message-Id: <199609291546.QAA25531@mserv1.dl.ac.uk> + To: bug-gnu-emacs@prep.ai.mit.edu + +Thu Aug 29 11:58:03 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * rcs2log: Update FSF address. + + * rcs2log: Be more aggressive about finding the author's full + name; try nismatch and ypmatch. + + * rcs2log: If the hostname appears not to be fully qualified, see + if domainname provides any useful information. + +Fri Aug 16 16:02:36 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * Makefile.in (installdirs): support this target + +Mon May 6 13:04:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (install): Don't tell user to run cvsinit. It isn't + called cvsinit anymore, and it isn't necessary (repositories are, + and need to be, compatible between cvs versions). + Sun Apr 14 11:30:36 1996 Karl Fogel <kfogel@floss.red-bean.com> * Removed pcl-cvs/ subdir; see tools/ subdir in the top-level from diff --git a/contrib/cvs/contrib/Makefile.in b/contrib/cvs/contrib/Makefile.in index a29dec0..2a6e9a2 100644 --- a/contrib/cvs/contrib/Makefile.in +++ b/contrib/cvs/contrib/Makefile.in @@ -12,12 +12,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# $CVSid: @(#)Makefile.in 1.6 94/10/22 $ - SHELL = /bin/sh srcdir = @srcdir@ @@ -47,10 +41,11 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ DISTFILES = \ ChangeLog README .cvsignore intro.doc \ - Makefile.in clmerge.pl cln_hist.pl commit_prep.pl cvs_acls.pl \ - cvscheck.sh cvscheck.man cvshelp.man descend.sh descend.man \ - dirfns.shar log.pl log_accum.pl mfpipe.pl rcs-to-cvs.sh rcs2log.sh \ - rcslock.pl sccs2rcs.csh rcs2sccs.sh + Makefile.in clmerge.pl cln_hist.pl commit_prep.pl cvs2vendor.sh \ + cvs_acls.pl cvscheck.sh cvscheck.man cvshelp.man descend.sh \ + descend.man dirfns.shar log.pl log_accum.pl mfpipe.pl rcs-to-cvs.sh \ + rcs2log.sh rcslock.pl sccs2rcs.csh rcs2sccs.sh \ + listen2.c listen2.mak # files installed in $(libdir)/cvs/contrib # @@ -59,8 +54,8 @@ CONTRIB_FILES = README intro.doc cvscheck.man # things we actually build and install.... # PROGS = rcs2log -CONTRIB_PROGS = clmerge cln_hist commit_prep cvs_acls cvscheck log log_accum \ - mfpipe rcs-to-cvs rcs2log rcslock sccs2rcs +CONTRIB_PROGS = clmerge cln_hist commit_prep cvs2vendor cvs_acls cvscheck \ + log log_accum mfpipe rcs-to-cvs rcs2log rcslock sccs2rcs .SUFFIXES: .pl .sh .csh @@ -84,7 +79,7 @@ CONTRIB_PROGS = clmerge cln_hist commit_prep cvs_acls cvscheck log log_accum \ all: Makefile $(PROGS) $(CONTRIB_PROGS) .PHONY: all -install: all $(libdir)/cvs/contrib +install: all installdirs for f in $(CONTRIB_FILES) ; do\ $(INSTALL_DATA) $(srcdir)/$$f $(libdir)/cvs/contrib/$$f; \ done @@ -94,11 +89,11 @@ install: all $(libdir)/cvs/contrib for f in $(PROGS) ; do\ $(INSTALL_PROGRAM) $$f $(bindir)/$$f; \ done - @echo "You might consider running 'cvsinit' to upgrade your repository(s)...." .PHONY: install -$(libdir)/cvs/contrib: +installdirs: $(top_srcdir)/mkinstalldirs $(libdir)/cvs/contrib +.PHONY: installdirs tags: .PHONY: tags diff --git a/contrib/cvs/contrib/README b/contrib/cvs/contrib/README index d453f8d..900b0c7 100644 --- a/contrib/cvs/contrib/README +++ b/contrib/cvs/contrib/README @@ -1,27 +1,31 @@ -$CVSid: @(#)README 1.12 94/09/25 $ +This "contrib" directory is a place holder for code/scripts sent to me +by contributors around the world. This README file will be kept +up-to-date from release to release. BUT, we must point out that these +contributions are really, REALLY UNSUPPORTED. In fact, we probably +don't even know what some of them really do. We certainly do not +guarantee to have tried them, or ported them to work with this CVS +distribution. If you have questions, your best bet is to contact the +original author, but you should not necessarily expect a reply, since +the author may not be available at the address given. -This "contrib" directory is a place holder for code/scripts sent to -me by contributors around the world. This README file will be kept -up-to-date from release to release. BUT, I must point out that these -contributions are really, REALLY UNSUPPORTED. In fact, I probably -don't even know what they do. Nor do I guarantee to have tried them, -or ported them to work with this CVS distribution. If you have questions, -you might contact the author, but you should not necessarily expect -a reply. USE AT YOUR OWN RISK -- and all that stuff. +USE AT YOUR OWN RISK -- and all that stuff. -"Unsupported" also means that noone has volunteered to accept and -check in changes to this directory. So submissions for new scripts to -add here are unlikely to be accepted. Suggested changes to the -existing scripts here conceivably might, but that isn't clear either. -The exception is pcl-cvs; that is more actively maintained (see -pcl-cvs/README). If you have some software that works with CVS that -you wish to offer it is suggested that you make it available by FTP or -HTTP and then announce it on the info-cvs mailing list. There is also -a web page of software related to CVS at -http://www.loria.fr/~molli/cvs-index.html which would presumably be -willing to list your software. +"Unsupported" also means that no one has volunteered to accept and check +in changes to this directory. So submissions for new scripts to add +here are unlikely to be accepted. Suggested changes to the existing +scripts here conceivably might, but that isn't clear either, unless of +course they come from the original author of the script. -Contents of this directory: +If you have some software that works with CVS that you wish to offer it +is suggested that you make it available by FTP or HTTP and then announce +it on the info-cvs mailing list. + +There is a web page of software related to CVS at the following URL which +would presumably be willing to list your software. + + http://www.loria.fr/~molli/cvs-index.html + +An attempt at a table of Contents for this directory: README This file. log A perl script suitable for including in your @@ -29,9 +33,6 @@ Contents of this directory: changes. Includes the RCS revision of the change as part of the log. Contributed by Kevin Samborn <samborn@sunrise.com>. - pcl-cvs A directory that contains GNU Emacs lisp code which - implements a CVS-mode for emacs. - Contributed by Per Cederqvist <ceder@lysator.liu.se>. commit_prep A perl script, to be combined with log_accum.pl, to log_accum provide for a way to combine the individual log messages of a multi-directory "commit" into a @@ -104,3 +105,7 @@ Contents of this directory: by hostname, then runs a subprocess whose input/output is redirected through the port. Contributed by Benjamin J. Lee <benjamin@cyclic.com> + cvs2vendor A shell script to move changes from a repository + that was started without a vendor branch to one + that has a vendor branch. + Contributed by Greg A. Woods <woods@planix.com> diff --git a/contrib/cvs/contrib/ccvs-rsh.pl b/contrib/cvs/contrib/ccvs-rsh.pl new file mode 100644 index 0000000..8cfc674 --- /dev/null +++ b/contrib/cvs/contrib/ccvs-rsh.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl + +# The version of the remote shell program on some Linuxes, at least, +# misuses GNU getopt in such a way that it plucks arguments to rsh +# that look like command-line switches from anywhere in rsh's +# arguments. This is the Wrong Thing to do, and causes older versions +# of CCVS to break. + +# In addition, if we live behind a firewall and have to construct a +# "pipeline" of rshes through different machines in order to get to +# the outside world, each rshd along the way undoes the hard work CCVS +# does to put the command to be executed at the far end into a single +# argument. Sigh. + +# This script is a very minimal wrapper to rsh which makes sure that +# the commands to be executed remotely are packed into a single +# argument before we call exec(). It works on the idea of a "proxy +# chain", which is a set of machines you go through to get to the CCVS +# server machine. + +# Each host you go through before you reach the CCVS server machine +# should have a copy of this script somewhere (preferably accessible +# directly from your PATH envariable). In addition, each host you go +# through before you reach the firewall should have the CVS_PROXY_HOST +# envariable set to the next machine in the chain, and CVS_PROXY_USER +# set if necessary. + +# This really isn't as complex as it sounds. Honest. + +# Bryan O'Sullivan <bos@serpentine.com> April 1995 + +$usage = "usage: ccvs-rsh hostname [-l username] command [...]\n"; + +if ($#ARGV < 1) { + print STDERR $usage; + exit 1; +} + +# Try to pick a sane version of the remote shell command to run. This +# only understands BSD and Linux machines; if your remote shell is +# called "remsh" under some System V (e.g. HP-SUX), you should edit +# the line manually to suit yourself. + +$rsh = (-x "/usr/ucb/rsh") ? "/usr/ucb/rsh" : "/usr/bin/rsh"; + +# If you are not rshing directly to the CCVS server machine, make the +# following variable point at ccvs-rsh on the next machine in the +# proxy chain. If it's accessible through the PATH envariable, you +# can just set this to "ccvs-rsh". + +$ccvs_rsh = "ccvs-rsh"; + +# There shouldn't be any user-serviceable parts beyond this point. + +$host = $ARGV[0]; + +if ($ARGV[1] eq "-l") { + if ($#ARGV < 3) { + print STDERR $usage; + exit 1; + } + $user = $ARGV[2]; + $cbase = 3; +} else { + $cbase = 1; +} + +# You might think you shoul be able to do something like +# $command = join(' ', $ARGV[$cbase..$#ARGV]); +# to achieve the effect of the following block of code, but it doesn't +# work under Perl 4 on Linux, at least. Sigh. + +$command = $ARGV[$cbase]; +for ($cbase++; $cbase <= $#ARGV; $cbase++) { + $command .= " " . $ARGV[$cbase]; +} + +if (defined $ENV{"CVS_PROXY_HOST"}) { + $command = (defined $user) + ? "$ccvs_rsh $host -l $user $command" + : "$ccvs_rsh $host $command"; + + if (defined $ENV{"CVS_PROXY_USER"}) { + exec ($rsh, $ENV{"CVS_PROXY_HOST"}, "-l", $ENV{"CVS_PROXY_USER"}, + $command); + } else { + exec ($rsh, $ENV{"CVS_PROXY_HOST"}, $command); + } +} elsif (defined $user) { + exec ($rsh, $host, "-l", $user, $command); +} else { + if (defined $ENV{"CVS_PROXY_USER"}) { + exec ($rsh, $host, "-l", $ENV{"CVS_PROXY_USER"}, $command); + } else { + exec ($rsh, $host, $command); + } +} diff --git a/contrib/cvs/contrib/cln_hist.pl b/contrib/cvs/contrib/cln_hist.pl index ff49d0a..19a0b81 100644 --- a/contrib/cvs/contrib/cln_hist.pl +++ b/contrib/cvs/contrib/cln_hist.pl @@ -1,7 +1,6 @@ #! xPERL_PATHx # -*-Perl-*- # -# $Id: cln_hist.pl,v 1.2 1995/07/10 02:01:26 kfogel Exp $ # Contributed by David G. Grubbs <dgg@ksr.com> # # Clean up the history file. 10 Record types: MAR OFT WUCG diff --git a/contrib/cvs/contrib/commit_prep.pl b/contrib/cvs/contrib/commit_prep.pl index 5272c04..bf0ce92 100644 --- a/contrib/cvs/contrib/commit_prep.pl +++ b/contrib/cvs/contrib/commit_prep.pl @@ -1,7 +1,6 @@ #! xPERL_PATHx # -*-Perl-*- # -#ident "@(#)cvs/contrib:$Name: $:$Id: commit_prep.pl,v 1.2 1995/07/10 02:01:29 kfogel Exp $" # # Perl filter to handle pre-commit checking of files. This program # records the last directory where commits will be taking place for diff --git a/contrib/cvs/contrib/cvs2vendor.sh b/contrib/cvs/contrib/cvs2vendor.sh new file mode 100644 index 0000000..234f4d9 --- /dev/null +++ b/contrib/cvs/contrib/cvs2vendor.sh @@ -0,0 +1,142 @@ +#! /bin/sh +# +# cvs2vendor - move revsisions from files in A to files in B +# +# The primary reason for this script is to move deltas from a +# non-vendor branched repository onto a fresh vendor branched one, +# skipping the initial checkin in assumption that it is the same in +# both repositories. This way you can take a project that was moved +# into CVS without the benefit of the vendor branch and for all +# intents and purposes add the vendor branch underneath the existing +# deltas. +# +# This script is also a decent example of repository maintenance using +# raw RCS commands (if I do say so myself! ;-). +# +# Tags are preserved. +# +# The timestamp of the initial vendor branch revision will be adjusted +# to be the same as the 1.1 revision of each source file. +# +# Extra branches in the source directory will cause breakage. +# +# Intermediate files are created in the current working directory +# where this script is started. +# +# Written by Greg A. Woods <woods@planix.com>, based on rcs2sccs +# (retains some of the rlog parsing from it). +# +# The copyright is in the Public Domain. +# + +if [ $# -ne 2 ]; then + echo USAGE: $0 srcdir dstdir + exit 2 +fi +tsrcdir=$1 +tdstdir=$2 + +revfile=/tmp/cvs2vendor_$$_rev +rm -f $revfile + +commentfile=/tmp/cvs2vendor_$$_comment +rm -f $commentfile + +srcdirs=`cd $tsrcdir && find . -type d -print | sed 's~^\.[/]*~~'` + +# the "" is a trick to get $tsrcdir itself without resorting to '.' +for ldir in "" $srcdirs; do + + srcdir=$tsrcdir/$ldir + dstdir=$tdstdir/$ldir + + # Loop over every RCS file in srcdir + # + for vfile in $srcdir/*,v; do + # get rid of the ",v" at the end of the name + file=`echo $vfile | sed -e 's/,v$//'` + bfile=`basename $file` + + if [ ! -d $dstdir ]; then + echo "making locally added directory $dstdir" + mkdir -p $dstdir + fi + if [ ! -f $dstdir/$bfile,v ]; then + echo "copying locally added file $dstdir/$bfile ..." + cp $vfile $dstdir + continue; + fi + + # work on each rev of that file in ascending order + rlog $file | grep "^revision [0-9][0-9]*\." | awk '{print $2}' | sed -e 's/\./ /g' | sort -n -u +0 +1 +2 +3 +4 +5 +6 +7 +8 | sed -e 's/ /./g' > $revfile + + for rev in `cat $revfile`; do + + case "$rev" in + 1.1) + newdate=`rlog -r$rev $file | grep "^date: " | awk '{printf("%s.%s\n",$2,$3); exit}' | sed -e 's~/~.~g' -e 's/:/./g' -e 's/;//' -e 's/^19//'` + olddate=`rlog -r1.1.1.1 $dstdir/$bfile | grep "^date: " | awk '{printf("%s.%s\n",$2,$3); exit}' | sed -e 's~/~.~g' -e 's/:/./g' -e 's/;//' -e 's/^19//'` + sed "s/$olddate/$newdate/" < $dstdir/$bfile,v > $dstdir/$bfile.x + mv -f $dstdir/$bfile.x $dstdir/$bfile,v + chmod -w $dstdir/$bfile,v + symname=`rlog -h $file | sed -e '1,/^symbolic names:/d' -e 's/[ ]*//g' | awk -F: '$2 == "'"$rev"'" {printf("-n%s:1.1.1.1\n",$1)}'` + if [ -n "$symname" ]; then + echo "tagging $file with $symname ..." + rcs $symname $dstdir/$bfile,v + if [ $? != 0 ]; then + echo ERROR - rcs $symname $dstdir/$bfile,v + exit 1 + fi + fi + continue # skip first rev.... + ;; + esac + + # get a lock on the destination local branch tip revision + co -r1 -l $dstdir/$bfile + if [ $? != 0 ]; then + echo ERROR - co -r1 -l $dstdir/$bfile + exit 1 + fi + rm -f $dstdir/$bfile + + # get file into current dir and get stats + date=`rlog -r$rev $file | grep "^date: " | awk '{printf("%s %s\n",$2,$3); exit}' | sed -e 's/;//'` + author=`rlog -r$rev $file | grep "^date: " | awk '{print $5; exit}' | sed -e 's/;//'` + + symname=`rlog -h $file | sed -e '1,/^symbolic names:/d' -e 's/[ ]*//g' | awk -F: '$2 == "'"$rev"'" {printf("-n%s\n",$1)}'` + + rlog -r$rev $file | sed -e '/^branches: /d' -e '1,/^date: /d' -e '/^===========/d' | awk '{if ((total += length($0) + 1) < 510) print $0}' > $commentfile + + echo "==> file $file, rev=$rev, date=$date, author=$author $symname" + + co -p -r$rev $file > $bfile + if [ $? != 0 ]; then + echo ERROR - co -p -r$rev $file + exit 1 + fi + + # check file into vendor repository... + ci -f -m"`cat $commentfile`" -d"$date" $symname -w"$author" $bfile $dstdir/$bfile,v + if [ $? != 0 ]; then + echo ERROR - ci -f -m"`cat $commentfile`" -d"$date" $symname -w"$author" $bfile $dstdir/$bfile,v + exit 1 + fi + rm -f $bfile + + # set the default branch to the trunk... + # XXX really only need to do this once.... + rcs -b1 $dstdir/$bfile + if [ $? != 0 ]; then + echo ERROR - rcs -b1 $dstdir/$bfile + exit 1 + fi + done + done +done + +echo cleaning up... +rm -f $commentfile +echo " Conversion Completed Successfully" + +exit 0 diff --git a/contrib/cvs/contrib/cvs_acls.pl b/contrib/cvs/contrib/cvs_acls.pl index bcb544d..c1d64e9 100644 --- a/contrib/cvs/contrib/cvs_acls.pl +++ b/contrib/cvs/contrib/cvs_acls.pl @@ -1,8 +1,6 @@ #! xPERL_PATHx # -*-Perl-*- # -# $Id: cvs_acls.pl,v 1.2 1995/07/10 02:01:33 kfogel Exp $ -# # Access control lists for CVS. dgg@ksr.com (David G. Grubbs) # # CVS "commitinfo" for matching repository names, running the program it finds diff --git a/contrib/cvs/contrib/cvscheck.man b/contrib/cvs/contrib/cvscheck.man index 61a064a..2b90b49 100644 --- a/contrib/cvs/contrib/cvscheck.man +++ b/contrib/cvs/contrib/cvscheck.man @@ -1,4 +1,3 @@ -.\" $Id: cvscheck.man,v 1.1.1.3 1995/08/28 16:20:24 jimb Exp $ .\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net> .TH CVSCHECK LOCAL "4 March 1991" FLUKE .SH NAME diff --git a/contrib/cvs/contrib/cvscheck.sh b/contrib/cvs/contrib/cvscheck.sh index 96dba6e..f711b43 100644 --- a/contrib/cvs/contrib/cvscheck.sh +++ b/contrib/cvs/contrib/cvscheck.sh @@ -1,5 +1,4 @@ #! /bin/sh -# $Id: cvscheck.sh,v 1.1 1995/07/10 02:26:29 kfogel Exp $ # # cvscheck - identify files added, changed, or removed # in CVS working directory diff --git a/contrib/cvs/contrib/cvshelp.man b/contrib/cvs/contrib/cvshelp.man index 2cfae1f..b166af6 100644 --- a/contrib/cvs/contrib/cvshelp.man +++ b/contrib/cvs/contrib/cvshelp.man @@ -1,4 +1,3 @@ -.\" $Id: cvshelp.man,v 1.1.1.3 1995/08/28 16:20:28 jimb Exp $ .\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net> .\" Full space in nroff; half space in troff .de SP diff --git a/contrib/cvs/contrib/descend.man b/contrib/cvs/contrib/descend.man index 5ac46f4..0434ca8 100644 --- a/contrib/cvs/contrib/descend.man +++ b/contrib/cvs/contrib/descend.man @@ -1,4 +1,3 @@ -.\" $Id: descend.man,v 1.1.1.3 1995/08/28 16:20:31 jimb Exp $ .TH DESCEND 1 "31 March 1992" .SH NAME descend \- walk directory tree and execute a command at each node diff --git a/contrib/cvs/contrib/descend.sh b/contrib/cvs/contrib/descend.sh index e6a7880..039a7a3 100644 --- a/contrib/cvs/contrib/descend.sh +++ b/contrib/cvs/contrib/descend.sh @@ -1,5 +1,4 @@ #! /bin/sh -# $Id: descend.sh,v 1.1 1995/07/10 02:26:32 kfogel Exp $ # # descend - walk down a directory tree and execute a command at each node diff --git a/contrib/cvs/contrib/listen2.c b/contrib/cvs/contrib/listen2.c new file mode 100644 index 0000000..09226bd --- /dev/null +++ b/contrib/cvs/contrib/listen2.c @@ -0,0 +1,65 @@ +/* This will develop into the inted-like program which + we may want to use for a server on Win95/NT. Right now + it is just a test program ("telnet foo 2401" and you'll + get a message). */ + +#include <winsock.h> +#include <stdio.h> + +int +main () +{ + struct sockaddr_in sa; + SOCKET t; + SOCKET s; + WSADATA data; + + if (WSAStartup (MAKEWORD (1, 1), &data)) + { + fprintf (stderr, "cvs: unable to initialize winsock\n"); + exit (1); + } + + t = socket (PF_INET, SOCK_STREAM, 0); + if (t == INVALID_SOCKET) + { + printf ("Error in socket(): %d\n", WSAGetLastError ()); + exit (1); + } + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = INADDR_ANY; + sa.sin_port = htons (2401); + if (bind (t, (struct sockaddr *) &sa, sizeof (sa)) != 0) + { + printf ("Cannot bind(): %d\n", WSAGetLastError ()); + exit (1); + } + if (listen (t, 1) != 0) + { + printf ("Cannot listen(): %d\n", WSAGetLastError ()); + exit (1); + } + while (1) + { + int sasize = sizeof (sa); + s = accept (t, (struct sockaddr *) &sa, &sasize); + if (s == INVALID_SOCKET) + { + printf ("Cannot accept(): %d\n", WSAGetLastError ()); + exit (1); + } + if (send (s, "hello, world\n", 13, 0) == SOCKET_ERROR) + { + /* Note that we do not detect the case in which we sent + less than the requested number of bytes. */ + printf ("Cannot send(): %d\n", WSAGetLastError ()); + exit (1); + } + if (closesocket (s) != 0) + { + printf ("Cannot closesocket(): %d\n", WSAGetLastError ()); + exit (1); + } + } + return 0; +} diff --git a/contrib/cvs/contrib/listen2.mak b/contrib/cvs/contrib/listen2.mak new file mode 100644 index 0000000..a72fd08 --- /dev/null +++ b/contrib/cvs/contrib/listen2.mak @@ -0,0 +1,161 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 40001
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=listen2 - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to listen2 - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "listen2 - Win32 Release" && "$(CFG)" !=\
+ "listen2 - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "listen2.mak" CFG="listen2 - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "listen2 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "listen2 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+RSC=rc.exe
+CPP=cl.exe
+
+!IF "$(CFG)" == "listen2 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL :
+
+CLEAN :
+ -@erase
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\
+ /Fp"$(INTDIR)/listen2.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/listen2.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/listen2.pdb" /machine:I386\
+ /out:"$(OUTDIR)/listen2.exe"
+LINK32_OBJS=
+
+!ELSEIF "$(CFG)" == "listen2 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL :
+
+CLEAN :
+ -@erase
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\
+ /Fp"$(INTDIR)/listen2.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/listen2.bsc"
+BSC32_SBRS=
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/listen2.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/listen2.exe"
+LINK32_OBJS=
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "listen2 - Win32 Release"
+# Name "listen2 - Win32 Debug"
+
+!IF "$(CFG)" == "listen2 - Win32 Release"
+
+!ELSEIF "$(CFG)" == "listen2 - Win32 Debug"
+
+!ENDIF
+
+# End Target
+# End Project
+################################################################################
diff --git a/contrib/cvs/contrib/log.pl b/contrib/cvs/contrib/log.pl index 5e3bf48..e4fb9b1 100644 --- a/contrib/cvs/contrib/log.pl +++ b/contrib/cvs/contrib/log.pl @@ -1,8 +1,6 @@ #! xPERL_PATHx # -*-Perl-*- # -#ident "$CVSid$" -# # XXX: FIXME: handle multiple '-f logfile' arguments # # XXX -- I HATE Perl! This *will* be re-written in shell/awk/sed soon! diff --git a/contrib/cvs/contrib/log_accum.pl b/contrib/cvs/contrib/log_accum.pl index d5c6783..d299fa6 100644 --- a/contrib/cvs/contrib/log_accum.pl +++ b/contrib/cvs/contrib/log_accum.pl @@ -1,8 +1,6 @@ #! xPERL_PATHx # -*-Perl-*- # -#ident "@(#)ccvs/contrib:$Name: $:$Id: log_accum.pl,v 1.4 1996/03/06 15:27:09 woods Exp $" -# # Perl filter to handle the log messages from the checkin of files in # a directory. This script will group the lists of files by log # message, and mail a single consolidated log message at the end of @@ -22,6 +20,7 @@ # -M modulename - set module name to "modulename" # -f logfile - write commit messages to logfile too # -s - *don't* run "cvs status -v" for each file +# -w - show working directory with log message # # Configurable options @@ -173,9 +172,10 @@ sub read_logfile { sub build_header { local($header); local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); - $header = sprintf("CVSROOT:\t%s\nModule name:\t%s\nChanges by:\t%s@%s\t%02d/%02d/%02d %02d:%02d:%02d", + $header = sprintf("CVSROOT:\t%s\nModule name:\t%s\nRepository:\t%s\nChanges by:\t%s@%s\t%02d/%02d/%02d %02d:%02d:%02d", $cvsroot, $modulename, + $dir, $login, $hostdomain, $year%100, $mon+1, $mday, $hour, $min, $sec); @@ -228,7 +228,6 @@ sub mail_notification { print MAIL "Date: " . $rfc822date . "\n"; print MAIL "Subject: CVS Update: " . $modulename . "\n"; print MAIL "To: " . $mailto . "\n"; - print MAIL "From: " . $login . "@" . $hostdomain . "\n"; print MAIL "Reply-To: " . $replyto . "\n"; print MAIL "\n"; print MAIL join("\n", @text), "\n"; @@ -255,9 +254,13 @@ $state = $STATE_NONE; $login = getlogin || (getpwuid($<))[0] || "nobody"; chop($hostname = `hostname`); chop($domainname = `domainname`); +if ($domainname !~ '^\..*') { + $domainname = '.' . $domainname; +} $hostdomain = $hostname . $domainname; $cvsroot = $ENV{'CVSROOT'}; -$do_status = 1; +$do_status = 1; # moderately useful +$show_wd = 0; # useless in client/server $modulename = ""; # parse command line arguments (file list is seen as one arg) @@ -284,6 +287,8 @@ while (@ARGV) { $modulename = shift @ARGV; } elsif ($arg eq '-s') { $do_status = 0; + } elsif ($arg eq '-w') { + $show_wd = 1; } elsif ($arg eq '-f') { ($commitlog) && die("Too many '-f' args\n"); $commitlog = shift @ARGV; @@ -303,10 +308,14 @@ if ($replyto eq '') { # @path = split('/', $files[0]); -# XXX there are some ugly assumptions in here about module names and +# XXX There are some ugly assumptions in here about module names and # XXX directories relative to the $CVSROOT location -- really should # XXX read $CVSROOT/CVSROOT/modules, but that's not so easy to do, since # XXX we have to parse it backwards. +# XXX +# XXX Fortunately it's relatively easy for the user to specify the +# XXX module name as appropriate with a '-M' via the directory +# XXX matching in loginfo. # if ($modulename eq "") { $modulename = $path[0]; # I.e. the module name == top-level dir @@ -384,8 +393,10 @@ while (<STDIN>) { chop; # Drop the newline if (/^In directory/) { - push(@log_lines, $_); - push(@log_lines, ""); + if ($show_wd) { # useless in client/server mode + push(@log_lines, $_); + push(@log_lines, ""); + } next; } diff --git a/contrib/cvs/contrib/mfpipe.pl b/contrib/cvs/contrib/mfpipe.pl index bae7a72..c74d715 100644 --- a/contrib/cvs/contrib/mfpipe.pl +++ b/contrib/cvs/contrib/mfpipe.pl @@ -12,9 +12,6 @@ # Especially if they regularly beat on the same directory. Anyway if you # think anyone would be interested here it is. # -# $Id: mfpipe.pl,v 1.2 1995/07/10 02:01:57 kfogel Exp $ -# -# # File: mfpipe # # Author: John Clyne diff --git a/contrib/cvs/contrib/rcs-to-cvs.sh b/contrib/cvs/contrib/rcs-to-cvs.sh index 3af83d7..66a62a9 100644 --- a/contrib/cvs/contrib/rcs-to-cvs.sh +++ b/contrib/cvs/contrib/rcs-to-cvs.sh @@ -1,6 +1,5 @@ #! /bin/sh # -# $Id: rcs-to-cvs.sh,v 1.2 1995/07/15 03:40:34 jimb Exp $ # Based on the CVS 1.0 checkin csh script. # Contributed by Per Cederqvist <ceder@signum.se>. # Rewritten in sh by David MacKenzie <djm@cygnus.com>. @@ -33,7 +32,7 @@ usage="Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository" vbose=0 message="" -message_file=/usr/tmp/checkin.$$ +if [ -d /var/tmp ]; then message_file=/var/tmp/checkin.$$; else message_file=/usr/tmp/checkin.$$; fi got_one=0 if [ $# -lt 1 ]; then @@ -79,7 +78,7 @@ fi if [ $got_one -eq 0 ]; then echo "Please Edit this file to contain the RCS log information" >$message_file echo "to be associated with this directory (please remove these lines)">>$message_file - ${EDITOR-/usr/ucb/vi} $message_file + ${EDITOR-vi} $message_file got_one=1 fi diff --git a/contrib/cvs/contrib/rcs2log.sh b/contrib/cvs/contrib/rcs2log.sh index ccea907..5548566 100644 --- a/contrib/cvs/contrib/rcs2log.sh +++ b/contrib/cvs/contrib/rcs2log.sh @@ -12,8 +12,6 @@ # Author: Paul Eggert <eggert@twinsun.com> -# $Id: rcs2log.sh,v 1.2 1995/07/28 19:48:45 eggert Exp $ - # Copyright 1992, 1993, 1994, 1995 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify @@ -27,8 +25,9 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# along with this program; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. tab=' ' nl=' @@ -122,7 +121,7 @@ month_data=' # log the revisions checked in since the first ChangeLog entry. case $rlog_options in '') - date=1970 + date=1970-01-01 if test -s ChangeLog then # Add 1 to seconds to avoid duplicating most recent log. @@ -158,8 +157,7 @@ case $rlog_options in } } } - # Output comma instead of space to avoid CVS 1.5 bug. - printf "%d/%02d/%02d,%02d:%02d:%02d\n", year,i+1,dd,hh,mm,ss + printf "%02d/%02d/%d %02d:%02d:%02d\n", i+1,dd,year,hh,mm,ss exit } ' @@ -364,7 +362,13 @@ EOF ' initialize_fullname=` - (cat /etc/passwd; ypmatch $authors passwd) 2>/dev/null | + ( + cat /etc/passwd + for author in $authors + do nismatch $author passwd.org_dir + done + ypmatch $authors passwd + ) 2>/dev/null | $AWK -F: "$awkscript" `$initialize_fullname esac @@ -414,6 +418,15 @@ case $hostname in echo >&2 "$0: cannot deduce hostname" exit 1 } + + case $hostname in + *.*) ;; + *) + domainname=`(domainname) 2>/dev/null` && + case $domainname in + *.*) hostname=$hostname.$domainname + esac + esac esac diff --git a/contrib/cvs/contrib/rcs2sccs.sh b/contrib/cvs/contrib/rcs2sccs.sh index af70138..b089dfe 100644 --- a/contrib/cvs/contrib/rcs2sccs.sh +++ b/contrib/cvs/contrib/rcs2sccs.sh @@ -1,8 +1,6 @@ #! /bin/sh # # -# OrigId: rcs2sccs,v 1.12 90/10/04 20:52:23 kenc Exp Locker: kenc -# $Id: rcs2sccs.sh,v 1.1 1995/07/10 02:26:45 kfogel Exp $ ############################################################ # Error checking diff --git a/contrib/cvs/contrib/sccs2rcs.csh b/contrib/cvs/contrib/sccs2rcs.csh index 0f31893..a1dea01 100644 --- a/contrib/cvs/contrib/sccs2rcs.csh +++ b/contrib/cvs/contrib/sccs2rcs.csh @@ -42,8 +42,6 @@ # ...!harvard!cg-atla!viewlog!kenstir # # Various hacks made by Brian Berliner before inclusion in CVS contrib area. -# -# $Id: sccs2rcs.csh,v 1.1 1995/07/10 02:26:48 kfogel Exp $ #we'll assume the user set up the path correctly diff --git a/contrib/cvs/cvs-format.el b/contrib/cvs/cvs-format.el index cdbd842..06dcc62 100644 --- a/contrib/cvs/cvs-format.el +++ b/contrib/cvs/cvs-format.el @@ -1,11 +1,17 @@ ;; -*- lisp-interaction -*- ;; -*- emacs-lisp -*- ;; +;; Set emacs up for editing code using CVS indentation conventions. +;; See HACKING for more on what those conventions are. +;; To use, put in your .emacs: +;; (load "c-mode") +;; (load "cvs-format.el") +;; You need to load c-mode first or else when c-mode autoloads it will +;; clobber the settings from cvs-format.el. Using c-mode-hook perhaps would +;; be a cleaner way to handle that. Or see below about (set-c-style "BSD"). ;; -;; originally from... -;; Rich's personal .emacs file. feel free to copy. -;; -;; Last Mod Wed Feb 5 16:11:47 PST 1992, by rich@cygnus.com +;; Credits: Originally from the personal .emacs file of Rich Pixley, +;; then rich@cygnus.com, circa 1992. He sez "feel free to copy." ;; ;; @@ -75,7 +81,13 @@ ;;`c-label-offset' ;; Extra indentation for line that is a label, or case or default. - +;; This doesn't quite do the right thing for CVS switches, which use the +;; switch (foo) +;; { +;; case 0: +;; break; +;; style. But if one manually aligns the first case, then the rest +;; should work OK. (setq c-label-offset -4) ;;;; eof diff --git a/contrib/cvs/doc/ChangeLog b/contrib/cvs/doc/ChangeLog index 01efb1a..1ef10f4 100644 --- a/contrib/cvs/doc/ChangeLog +++ b/contrib/cvs/doc/ChangeLog @@ -1,3 +1,1299 @@ +Wed May 14 12:16:19 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Binary files): Add text and comment about + automatically detecting binary files. + +Mon May 12 11:55:07 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Connection and Authentication): Add item about + future expansion. + +Thu May 8 11:08:34 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Update imports): Add comment about wdiff + vs. fsf/wdiff in example. + +Wed May 7 13:52:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (checkout): Add comment about need for example + regarding what the "module" argument means. + +Tue May 6 18:02:27 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (History browsing): Add comment about looking at old + revisions. + +Tue May 06 15:05:00 1997 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo: More additions/corrections for -R due to recent + changes. + +Mon Dec 16 15:18:00 1996 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo: Added/corrected documentation for -R. (Minor edits + by Jim Kingdon to reflect recent changes in cvs.texinfo) + +Sun May 4 14:38:35 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Compatibility): Add comment about "D" lines in + Entries. + + * cvs.texinfo (CVS commands, diff): Change "run diffs" to "show + differences"; the former is jargon. + (CVS commands): Don't refer to "rlog" in describing what log does. + + * Makefile.in (cvsclient.dvi cvsclient.aux): Run texi2dvi rather + than (poorly) emulating it ourself. + + Fix overfull and underfull hboxes: + * cvs.texinfo (What is CVS?): Add words "the newsgroup" before + "comp.sources.unix". + (Credits): Put list of people in @display. + (Repository files): Put /usr/local/cvsroot in @example. + (Connecting via rsh): Change "anklet" to "toe" in example. + (Kerberos authenticated, Password authentication client, Password + authentication server): Change "brickyard" to "yard" in example. + (Read-only access): Use @example and refer to files with a shorter + pathname. + (Server temporary directory): Use @example for pathname. + (Watches Compatibility): Add phony line break. + (Revision numbers): Remove revision 1.2.2.2 and tighten up the + spacing for "the main trunk". + (Tags, Creating a branch): Change /usr/local/cvsroot to /u/cvsroot. + (Merging more than once): Tighten up spacing for "the main trunk". + (Recursive behavior): Put long command in @example. + (First import): Remove word "called". + (Common options): Put long URL in @example. + (loginfo example): Use fewer hyphens in example. + (Variables): Put long command name in @example. + (Copying): Add line break. + (Administrative files): Remove "the" from title. + (Copying): Change "@unnumberedsec" to two "@heading"s. + * cvsclient.texi (Requests): Change /home/kingdon/zwork/cvsroot to + /u/cvsroot. + (Example): Add word "file". + (Example): Change line breaks in example log message. + (Example): Change /home/kingdon/testing/cvsroot to /u/cvsroot. + + * cvs.texinfo (Credits): Don't refer to appendix A and B, they + have been renumbered. Reword so that it works whether the text in + question has since been rewritten or not. + + * cvs.texinfo (BUGS): Rewrite to reflect the many different ways + that one might want to handle bugs. Move information on Signum + and Cyclic from Preface to here. Remove information on known + deficiencies in the manual (some of them I'm not sure were really + things in need of improvement; others were too general to be + useful). For the most part FIXME comments are probably better for + this. Remove "Linkoping, October 1993, Per Cederqvist"--many + parts of the manual are now from other people, dates, and places. + (What is CVS): For the most part, just refer to BUGS concerning + bug-cvs. Also tell people how to subscribe to bug-cvs. + (Credits): Say that list is not comprehensive and refer to + ChangeLog. + +Sat May 3 10:51:58 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (rcsinfo): Add comment about checkoutlist and + related topics. + + * cvs.texinfo (Server temporary directory): New node. + + * cvs.texinfo (Backing up): New node. + + * cvs.texinfo (Repository): Be more explicit about the repository + and the working directory not being subdirectories of each other. + +Mon Apr 28 11:12:56 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Removing files): Use "*.c" not "?.c" in example; + the former should be good for both unix and DOS-like operating + systems. Document -f option. Refer to Invoking CVS for a full + list of options. Add a few comments. + + * cvs.texinfo (Invoking CVS): For checkout and update, call them + "sticky options" not "sticky kopts". + + * cvs.texinfo (Editing files): Add additional comments on get + vs. checkout. + +Sun Apr 27 16:17:06 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (commit): Only document the current flags (where -f + is force and -F file gets the message from a log file). We had + partly made this change on 9 Feb 1997, but some places got missed. + + * RCSFILES: Add discussion of the common concern regarding + applying deltas to get to a branch head. + + * DIFFUTILS-2.7-BUG: New file. + + * cvs.texinfo (File status): Refer to "Invoking CVS", not + "status", for status options. Add paragraph about how "cvs -n -q + update" is another way to display file status. + (update examples): Removed; it had contained the "cvs -n -q + update" material. + (Invoking CVS): xref to "File status" and "Tags", not "status" and + "status options". + (status, status options): Removed. + (update options, checkout options): xref to "Invoking CVS" + not "status". + + * cvsclient.texi (Requests): Clarify how long-lived Sticky and + Static-directory are. + + * cvs.texinfo: Add @finalout. + + * cvs.texinfo (Error messages): Add "cannot change permissions on + temporary directory" message. + +Wed Apr 23 12:53:45 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Requests): Document "add" in much more detail. + +Wed Apr 23 00:38:17 1997 Ian Lance Taylor <ian@cygnus.com> + + * cvsclient.texi (Requests): Correct small typo (`a' for `as'). + +Tue Apr 22 14:23:32 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Protocol Notes): Expand ideas on multisite + features somewhat. Add items about the network turnarounds for + pserver authentication and for protocol negotiation. + +Mon Apr 21 08:54:48 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Working directory storage): Describe what to do + with Entries.Log in more detail. + + * cvsclient.texi (Responses): Say "CVS 1.9 and earlier" rather + than "pre version 1.10". The latter increases confusion by + referring to a version which doesn't exist yet. + +Mon Apr 21 01:02:53 1997 Ian Lance Taylor <ian@cygnus.com> + + * cvsclient.texi (Responses): Document Rcs-diff. Indicate that + Patched is now deprecated in favor of Rcs-diff. + +Sun Apr 20 23:42:03 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Working directory storage): Add note about format + of timestamp and the "Result of merge" concept. + +Sat Apr 19 13:42:33 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Responses): It is OK for Copy-file to implement + a rename instead of a copy. + +Fri Apr 18 12:05:48 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Assigning revisions): Say that -r implies -f. + +Thu Apr 17 16:34:14 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (From other version control systems): Add comment + about CMZ and PATCHY. + +Wed Apr 16 12:35:25 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Responses): Add paragraph describing how + Copy-file relates to Merged. + (Responses): Add paragraph about how it is the server which + worries about not clobbering the user's file. + +Tue Apr 15 00:57:31 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * RCSFILES: Add notes on keyword expansion. + + * cvs.texinfo (Rename by copying): Comment out seemingly erroneous + text regarding the revision number that the new file starts with. + +Mon Apr 14 12:37:35 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Requests): Clients should try to send + notifications right away. + + * cvsclient.texi (Requests): For Notify request, clarify a few + future expansion situations. Specify the format of the time. + + * cvsclient.texi (Requests): Clarify that arguments to co, rdiff, + and rtag are module names (and how that differs from file/directory + names). + + * cvsclient.texi (Responses): Say that servers need to create + directories one at a time. + +Sat Apr 12 09:32:58 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Committing your changes): Say that editor default + is notepad (not vi) for Windows NT/95. Be more clear about what + "cvs commit" does. Add paragraph about timestamps. + (Environment variables, Global options, editinfo): + Add xrefs to that node. + +Thu Apr 10 15:48:39 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Error messages): Add "could not patch; will refetch". + +Wed Apr 9 15:21:11 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Working directory storage): New node. + + * cvs.texinfo (Error messages): Add comment about "cvs co ." on + NT. + +Tue Apr 8 14:44:26 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Error messages): Add diff3 usage message. + +Sun Apr 6 19:03:01 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Removing files): Add comment about undoing a "cvs + remove". + + * cvsclient.texi (Requests): Explicitly mention the idea of + deferring "Notify" requests. + +Tue Apr 1 07:51:38 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Responses): Add paragraph about directory + creation and empty directories. + + * cvs.texinfo (Binary files): Add comment about binary files and + merges. + + * cvsclient.texi (Requests): Add discussion of when to send + Is-modified. + + * cvsclient.texi (Requests): Sending Is-modified is enough to + prevent the file from being considered "lost". + +Sun Mar 30 00:31:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Requests): Add Is-modified request. Clarify + order of Entry relative to Unchanged or Is-modified (might as well + specify the same thing vis-a-vis Modified while we are at it). + +Sat Mar 29 12:32:40 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi: Change "newline" to "linefeed". Most of the + document already reads "linefeed" and that is what is intended. + (File transmissions): New node, moved here from Requests. + (Goals, Filenames, File transmissions, new node Strings): Add + discussion of character sets and what we expect from the transport + protocol we run on. + + * cvsclient.texi (Requests): Add paragraph about each Directory + request specifying a new local-directory and repository. + + * cvsclient.texi (Requests): Add paragraph about renaming + local-directory in Directory request. Use "local-directory" + consistently instead of "working directory", for clarity. + +Fri Mar 28 13:59:59 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Requests): Make it clear that there is no + guarantee that one will get Clear-sticky instead of another + response. Also clarify that clients will tend to store the + repository in a long-term way. + + * cvsclient.texi (Requests): Further clarify Directory example. + + * cvsclient.texi (Requests): Add example and further explanation + of what expand-modules is for. + + * cvsclient.texi (Requests): Add example, hopefully making it + clearer what REPOSITORY and LOCAL-DIRECTORY mean to Directory. + + * cvs.texinfo (Attic): New node. + (rtag options): Adjust discussion of -a accordingly. + (Repository files): Adjust accordingly. + +Thu Mar 27 09:57:05 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Error messages): Give exact wording of broken pipe + error message. + + * cvs.texinfo (history database): Add comment about various + problems with the history file. + + * cvs.texinfo (Common options): The ISO8601 web page we had + mentioned in a comment is no more. Replace it with a new one. + + * cvs.texinfo (Common options): "cvs history" also outputs dates. + +Wed Mar 26 10:54:21 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Common options): "cvs editors" also outputs dates. + + * cvs.texinfo (Outside): Fix paragraph which said that revision + numbers start at 1.0. First of all, it is 1.1. Second of all, it + is sometimes 2.1, 3.1, etc. Third of all, the xref should be to + Assigning revisions not commit options. + + * cvs.texinfo (Outside): Comment out sentence which incorrectly + stated that "cvs add" can operate on "foo/bar.c". + +Tue Mar 25 22:21:29 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Error messages): New node. + (Magic branch numbers): Move from Troubleshooting to Revisions and + branches. The former placement never made any sense to me. + (Revision numbers): Remove "Main trunk (intro)" index entry now + that this node is right next to the other "main trunk" index + entry. + (BUGS): Very briefly mention reporting bugs in CVS. + + * cvs.texinfo (Compatibility): Add comment about "Nfoo" in CVS/Tag. + +Mon Mar 24 13:50:24 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Creating a branch): Add comment about -r in branch + example. + + * cvsclient.texi (Responses): Discuss meaning of tagspec and + future expansion in Set-sticky. The behavior described is the one + which CVS has always implemented. + +Fri Mar 21 14:19:05 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Requests): Revise meaning of "Case" per change + to CVS. + +Tue Mar 18 15:50:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + The following reorganization hopefully presents numeric revisions + in a slightly more coherent fashion. The only new material is the + paragraph about assigning revisions for added files. + * cvs.texinfo (A sample session): Bring in a sentence from Basic + concepts node, defining a repository. + (Revisions and branches): Renamed from Branches (it has always + covered non-branch tags too). Bring in nodes "Revision numbers" and + "Versions revisions releases" from Basic concepts, the former in + particular was way too detailed for an intro section. + (A sample session): Add comment about how we need an introduction + and what might go into one. Also bring in the paragraph from + Basic concepts introducing modules, but comment it out. + (Viewing differences): Add comment about + (Basic concepts): Removed; its content has been farmed out as + described above, and as the comment said, it was fundamentally + flawed. + (Assigning revisions): New node. Incorporates the "New major + release number" subsubsec which was in "commit examples". Add + paragraph concerning how CVS assigns revisions on added files. + (commit options): Refer to that node under -r. + (Invoking CVS): Add comment about text for -r. + +Tue Mar 18 13:04:30 1997 Jim Meyering <meyering@totoro.cyclic.com> + + * Makefile.in: (install-info): Depend on installdirs. + +Sun Mar 16 12:37:12 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (File permissions): CVSUMASK now works for RCS + files; but it is (still) awkward for client/server CVS. + +Sat Mar 15 17:41:12 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Magic branch numbers): Add comment about where this + should go. + +Thu Mar 13 09:11:36 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Credits): Fix grammatical mistake ("manual about" + -> "manual is about"). Reported by Philippe De Muyter. + +Sun Mar 9 09:06:40 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (File permissions): Add comment about val-tags and + CVSUMASK. + +Sun Mar 2 12:33:26 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (From scratch): Add comment about creating + directories with add rather than import. + + * cvs.texinfo (Creating a repository): Add comment about how this + somewhat duplicates Server requirements. + + * cvs.texinfo (Connecting via rsh): Add comment about rsh + vs. remsh. Also wording fix ("incorrect" -> "inapplicable"). + + * cvs.texinfo (Outside): Add comment about renames and annotate. + + * cvs.texinfo (Server requirements): New node. + +Thu Feb 27 15:20:49 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Multiple developers): Reword section on "cvs admin + -l". As nearly as I can tell based on when it came up on info-cvs + and other contexts, people who are into reserved checkouts + generally find that cvs admin -l is OK. Add a bunch more notes + (inside @ignore) about reserved checkout implementation ideas. + +Sun Feb 23 16:12:03 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Common options): Add various additional comments + about date formats. + + * RCSFILES: Remove diff for Id and explain it in words instead. + The previous values for Id had been clobbered by keyword expansion + on the RCSFILES file itself. + +Sat Feb 22 14:16:28 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (DISTFILES): Fix typo (missing backslash). + +Fri Feb 21 23:08:38 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * RCSFILES: New file. + * Makefile.in (DISTFILES): Add RCSFILES. + +20 Feb 1997 Lenny Foner <foner@media.mit.edu> + + * cvs.texinfo (Checklist): Fix typo ("keword" -> "keyword"). + +Thu Feb 20 21:57:05 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Keeping a checked out copy): Add "web" to index. + +Wed Feb 12 18:44:16 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Password authentication client, Invoking CVS): + Document "cvs logout" command. + +Tue Feb 11 20:42:45 1997 Ian Lance Taylor <ian@cygnus.com> + + * cvs.texinfo (commit options): Document that the -f option to + commit disables recursion. + +Sun Feb 9 13:58:59 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (diff options): Document all the options we pass + through to diff. Remove paragraph about -D sometimes meaning + --ifdef since that is no longer true. + + * cvs.texinfo (Multiple developers): Add lengthy comment about + reserved checkout design issues. + + * cvs.texinfo (Wrappers): Add paragraph about timestamps. + + * cvs.texinfo (commit options): Don't try to document what CVS 1.3 + does with -f and how recent versions differ: 1.3 is pretty old + anyway, we generally only try to document the current version, and + the way it was described here was pretty confusing. + (Environment variables): Likewise for CVSEDITOR. + + * cvs.texinfo (import output): Add index entries for symbolic + links. Add brief mention of whether behavior should be + different. Add comments on other symbolic link issues. + +Wed Feb 5 13:02:37 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Concurrency): Add comment about commit/commit + atomicity. + +Mon Feb 3 10:55:41 1997 joel boutros <nihilis@moral.addiction.com> + + * cvs.texinfo (Connecting via rsh): Fix typo (programs -> problems). + +Fri Jan 31 12:18:47 1997 Ian Lance Taylor <ian@cygnus.com> + + * cvsclient.texi (Connection and Authentication): Correct typo + (``sent'' for ``send''), and rewrite sentence for clarity. + +Fri Jan 24 10:31:57 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (File status): Change "Unresolved Conflict" to "File + had conflicts on merge" per change to CVS. + +Sun Jan 19 16:21:17 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (admin): Add comments about "group" and "compiled in + value". At least one info-cvs poster was confused by this. + +Thu Jan 16 17:54:51 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Wrappers): It is just -t/-f which doesn't work + client/server. -k *does* (well, except for the problem with + import noted in BUGS). -m I don't know and I doubt anyone cares. + +Mon Jan 13 15:41:02 1997 Karl Fogel <kfogel@ynu38.ynu.edu.cn> + + * cvs.texinfo (Read-only access): rephrase to imply that there may + be other administrative files, besides history and locks, which + read-only users can also affect (in the future, for example, the + `passwd' file). + +Wed Jan 8 14:50:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in: Remove CVSid; we decided to get rid + of these some time ago. + +Wed Jan 8 09:08:36 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Connection and Authentication): Document + restriction that cvs root sent in the cvs protocol and in the + pserver authentication protocol must be identical. + +Thu Jan 2 13:30:56 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in, cvs.texinfo: Remove "675" paragraph; + see ../ChangeLog for rationale. + +Thu Jan 2 09:34:51 1997 Karl Fogel <kfogel@ynu38.ynu.edu.cn> + + * cvs.texinfo (Read-only access): new node. + (Repository): new menu item for above new node. + (Password authentication server): document the user-aliasing + feature. Why was this undocumented before? + +Wed Jan 1 18:12:11 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Conflicts example): Use @asis in example to prevent + starting a line with a conflict marker. This means that when + maintaining the file with CVS itself, CVS will not think there is + a conflict merely because of the conflict marker in the example. + IMHO, this is totally bogus and CVS needs a better way of figuring + out whether a conflict is resolved (see comments elsewhere in this + node), but until then.... Credit to Fred Fish for reporting the + problem. + + * cvs.texinfo (cvsignore): Add paragraph about how .cvsignore + files in the sources being imported by "cvs import" override + "-I !". Credit goes to Fred Fish for pointing out this problem. + +Thu Dec 19 12:36:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Credits): Update Roland Pesch email address per his + request. + +Tue Dec 17 12:57:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (verifymsg): In example, remove text "and reedit if + necessary"; it was copied from editinfo and doesn't apply here. + Fix syntax of if statement; remove unnecessary attempt at loop; + don't use -n with echo. Add @appendixsec at start of node. + Add note about how verifymsg cannot change log message. + (editinfo): In paragraph saying editinfo is obsolete, fix various + typos and formatting glitches. Mention -e as well as EDITOR. + (editinfo): In saying that editinfo doesn't get consulted with -m, + -F or client/server, recommend verifymsg. Remove comment which + says, in effect, "we need a feature like verifymsg". + (editinfo example): Change "verifymsg" back to "editinfo" here; + the example is of editinfo not verifymsg. + +Tue Dec 17 12:45:32 1996 Abe Feldman <feldman@cyclic.com> + + * cvs.texinfo (verifymsg): New node. + various places: Say that editinfo is obsolete, or refer to + verifymsg instead of editinfo + +Wed Dec 11 08:55:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Compatibility): Add comment about 1.3 and file death. + + * cvs.texinfo (update output, release output): Document "P" as + well as "U". + +Tue Dec 10 16:23:40 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Builds): Change "make" to "implement" and "build"; + in this context "make" is ambiguous. + (Builds): Add new URL of mk web page. + +Mon Dec 9 11:03:37 1996 Jim Blandy <jimb@floss.cyclic.com> + + * cvs.texinfo (Password authentication client, Environment + variables): Remove mention of CVS_PASSWORD. + +Sun Dec 8 22:38:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Repository files): Mention differences between RCS + files in RCS and in CVS. + (Tags): Tag names must start with a letter. + +Fri Dec 6 09:08:18 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (syntax): Expand discussion of regular expression + syntax. + +Fri Nov 29 09:06:41 1996 fnf@ninemoons.com (Fred Fish) + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo, cvsclient.texi: Make sure @ref and friends are + followed by "," or "." as described in the texinfo manual. This + is a dubious practice as texi2html and texinfo.tex don't require + it, and makeinfo could insert them as needed, but since makeinfo + doesn't do that yet, cope. + + * cvs.texinfo (From files): Suggest "diff -r" rather than "ls -R" + as the way to see that the sources seem to have been imported + correctly. + (Common options): -k is also available with import. + (admin options): Fix typo ("interrested" -> "interested"). + +Mon Nov 25 10:03:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Common options): Add comments about two digit + years, year 2000, and ambiguous/nonexistent dates. + +Sun Nov 24 17:27:24 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (First import): Don't say what the wdiff program we + are using as an example does--that is confusing. Also don't show + untarring it--people might be familiar with cpio, ZIP, VMS BACKUP, + etc., instead of tar. + + * cvs.texinfo (Adding files): Update comment about "cvs add -m". + + * cvs.texinfo (Common options): Remove -H; -H is not a command + option. + (Global options): Also list --help and --version. Don't say that + -H gives a list of commands; it doesn't any more (directly). + + * cvs.texinfo: Add comment pointing to paper size web page. + + * cvs.texinfo (Common options): Rewrite section on date formats. + Executive summary is that RFC822 and ISO8601 are now preferred. + +Wed Nov 20 08:39:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Getting Notified): Add paragraph clarifying that + watches happen per user, not per working directory. + +Tue Nov 19 09:39:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Tags): Suggest that future special tag names might + start with ".". Fix typo. + + * cvs.texinfo (Removing directories): -P is also available with + export. + (Moving directories): Rewrite first paragraph; now says that you + must use -P for the directory to disappear from working + directories. Thanks to Martin Lorentzon + <Martin.Lorentzson@emw.ericsson.se> for reporting this bug. + (various): Where we mention -P, point to Removing directories + node. + +Sat Nov 16 18:03:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Example): Rewrite to actually be based on a real + live example (and therefore reflect the way the protocol currently + works). Add comment about formatting of the document itself. + +Thu Nov 14 10:22:58 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Introduction): Use @ref, not @xref, after "see". + (Goals): Rewrite items about locking, about uploading in big + chunks, and about atomicity to be focused more on the protocol + than the current implementation. + (Notes): Remove this node. The attempt to describe the basic + model has pretty much been replaced by the Introduction. + The material about how to start the client is incomplete and + better left to cvs.texinfo. And the item about the lack of + SERVER_FLOWCONTROL is obsolete now that SERVER_FLOWCONTROL is the + default. + (Protocol Notes): Add comment about multisite features. + (Requirements): Use @code for requests and responses. + + * cvs.texinfo (Remote repositories): Add a few sentences defining + "client" and "server"; before we had been using the terms without + defining them. + + * cvs.texinfo (What is CVS?): Add paragraph about reporting bugs. + Reword and expand comp.software.config-mgmt description (and add + comments about other newsgroup facts). Point people at GNU list + of FTP sites rather than directly at prep.ai.mit.edu. + +Wed Nov 6 09:45:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Tracking sources): Add comment regarding added and + removed files. + +Tue Nov 5 14:00:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo: Rename node "Invoking CVS" to "CVS commands". + Rewrite the intro and comments to reflect addition of the new + Invoking CVS. + (Invoking CVS): New node, a quick summary of each command. + (annotate): Don't list the options; refer to Invoking CVS and + Common options instead. + +Sun Nov 3 21:22:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Compatibility): New node, moved from ../README. + + * cvs.texinfo (Common options): Add comment about how tar manual + contains documentation for getdate date formats. + +Fri Nov 1 14:00:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (commit examples): Rewrite "New major release + number" section to tighten up the wording, better motivate the + discussion, and replace the term "rcs revision number" with + "numeric revision". + +Fri Oct 25 07:49:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (loginfo): Don't say "a la printf"; the syntax is + only vaguely similar to printf. + + * cvs.texinfo (loginfo): To get just the repository name, suggest + %{} instead of % "standing alone"; the latter is now an error. + +Tue Oct 22 13:08:54 1996 Noel Cragg <noel@gargle.rain.org> + + * cvs.texinfo (loginfo): add information on the new loginfo format + string specification. + +Mon Oct 21 17:33:44 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Builds): New node. + (What is CVS?): Refer to it. + +Sat Oct 19 14:32:21 1996 Jim Meyering <meyering@asic.sc.ti.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Choosing a model): Wording/grammar fix. + +Sat Oct 19 14:32:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Obsolete): New node. + (Requests): Remove Repository and Lost and adjust Directory, + UseUnchanged, and other places accordingly. + (Required): Directory and Unchanged are now required. + + * cvs.texinfo (Removing files): Don't talk about modules; they are + not relevant in this context. + (Removing directories): New node. + (Common options): Refer to it instead of duplicating information. + +Fri Oct 18 11:05:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (First import, import): Add paragraph about the fact + that import doesn't modify the directory which it imports from. + + * cvs.texinfo (Creating a repository): Add paragraph about + resource requirements. + + * cvs.texinfo (Copying): Replace empty node with a copy of the GPL. + +Thu Oct 17 12:10:55 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Adding files): Revise comment to more accurately + reflect the functioning/nonfunctioning status of cvs add -m. + + * cvs.texinfo (Reverting local changes): New node, somewhat based + on the version of this node from 30 Sep 96 change. + (admin options): Refer to it. + + * cvs.texinfo: Reinstate 30 Sep 96 change from A4 to US letter. + + * cvs.texinfo (Concurrency): When telling people how to clean up + locks, tell them to make sure the locks are owned by the person + who has the stale locks. + (update output, release output): Remove text about how CVS doesn't + print "? foo" for directories; CVS has since been changed (see + conflicts-130 in sanity.sh). + +Wed Oct 16 15:01:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (history options): Mention new option -x E. + +Mon Oct 14 15:21:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Tags): Add paragraph on choosing a convention for + naming tags. + +Thu Oct 10 16:05:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (modules): Describe what & does. + +Mon Oct 7 17:20:11 1996 Ian Lance Taylor <ian@cygnus.com> + + * cvs.texinfo (Removing files): Correct apparent cut and paste + error: refer to the removed file, not the added file. + +Tue Oct 1 14:15:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo: Revert all recent changes (the last unscathed one + is the CVSUMASK one from Sunday). For the most part said changes + are for new features which are not appropriate at this stage of + the release process. None of the changes being reverted need to + go into 1.9, that is for sure. + +Mon Sep 30 18:17:34 1996 Greg A. Woods <woods@most.weird.com> + + * cvs.texinfo (Credits): add comment asking if we should update. + Add more detail about printing Letter on A4. + Add some comments about internal comments. + (From files): describe "cvs import -b 1" for importing existing + projects onto the main branch. + (First import): add a couple of helpful hints about naming vendor + and release tags, etc., and regularize the examples with this. + (Tracking sources): noted some reasons why you might use vendor + branches with "cvs import". + (Update imports): mention using "update" in place of "checkout" if + you have an existing working directory. + (Binary files in imports): add sub-menu separator comment. + (Tracking sources): new menu entry "Reverting to vendor release". + (Reverting to vendor release): new node to describe reverting + local changes and optionally using patch(1) to move local changes + forward. + (Global options): describe -D and -g, as well as DIFFBIN and + GREPBIN. + (export examples): add one. + (import options): describe the effect of '-b 1'. + +Mon Sep 30 08:09:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo: Adjust comments concerning A4 vs. US letter, + referring to ../README. + + * cvs.texinfo (Common options): Add comment about dates which CVS + uses in output. + +Sun Sep 29 11:14:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Keyword list): Don't mention Name twice. + + * cvs.texinfo (File permissions): Expand CVSUMASK stuff a bit. + (Setting a watch, Environment variables, Global options): Update + index entries for "read-only files, and ...". + + * cvsclient.texi (Requests): State that Gzip-stream is preferred + to gzip-file-contents. Cite RFC1952/1951 rather than just "gzip". + Say that RFC1950/1951 compression is also known as "zlib". + +Sat Sep 28 09:31:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Repository): Move all information about the + internal structure of the repository to User modules node. Rename + it to "Repository storage" ("User modules" wasn't particularly + clear). Mention CVSUMASK. Much clarification and + reorganization. + (Basic concepts): Remove material which duplicates what is now in + Repository. Rewrite paragraph introducing modules. + + * cvs.texinfo (Starting a new project): In discussing difficulty + in renaming files, don't refer to "cvs 1.x"--there is no + non-vaporous "cvs 2.x". Reword to reflect that part of the reason + to avoid renames (where possible) is not because of CVS at all, and + to try to give a general impression of how bad CVS issues involved in + renaming are. + +Fri Sep 27 04:23:44 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Adding files): Talk about directories, not modules, + since that is what is meant. Suggest using -kb option to add + rather than running cvs admin after the fact and xref to Binary + files not admin examples. Incorporate information which had been + in "add" node (there was a lot of duplication). Don't document + use of "add" on a directory to take the place of "cvs update -d"; + the latter is simpler and more logical. + (add, add options, add examples): Removed. + (release output, release options): Update xrefs accordingly. + (Adding files, Removing files): Mention the fact that adds and + removes are branch-specific. + (Merging adds and removals): New node. + + * cvs.texinfo (Concurrency): When mentioning RCS locks, use the + term reserved checkouts and xref to the place where we discuss + them in more depth. + +Thu Sep 26 08:26:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (log): Add comments about timezones. + (log, Common options): Add index entries for timezone and zone, time. + +Wed Sep 25 11:05:30 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (log options): Add xref to where we describe the + date formats that -d accepts. + (Common options): Don't refer to date formats accepted by co(1); + CVS's rules have never been the same. Add long whiny comment + about what a mess date formats are. + +Tue Sep 24 11:49:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (From other version control systems): The RCS file + must not be locked when you copy it to the CVS repository. + + * cvs.texinfo (Editing files): Also discuss how to revert in the + non-watch case. Add some index entries. + + * cvs.texinfo (update output): Add comment about how we *should* + be handling .# files. Mention fact that it is different under + VMS. Add .# to index. + +Fri Sep 20 13:08:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Multiple developers): Revise text on reserved + versus unreserved checkouts extensively. Move index entries for + "reserved checkouts" and "RCS-style locking" to here. Add + cross-reference to cvs admin -l. Add new section "Choosing a + model". + (Editing files): Add note about use of the word "checkout". + +Tue Sep 17 00:54:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Defining the module): Don't suggest "cvs co + modules"; that depends on a "modules" module being defined which + is not the default which is created by "cvs init". Instead + suggest "cvs co CVSROOT/modules" which should always work. + +Tue Sep 17 00:43:49 1996 VaX#n8 <vax@linkdead.paranoia.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Rename by copying): Suggest "cvs tag -d" on the file + "new", not on everything. Also don't suggest deleting branch tags. + +Tue Sep 17 00:34:39 1996 David A. Swierczek <swierczekd@med.ge.com> + + * Makefile.in (install-info): Note whether files are in srcdir and + deal with it rather than cd'ing into srcdir. + +Mon Sep 16 23:33:36 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Wrappers): Add comment about using wrappers to + compress files in the repository. + + * cvs.texinfo (modules): Add comments about how we should be + documenting how -i and friends operate in client/server CVS. + + * cvs.texinfo (File permissions): Describe the need for write + permissions for locks and val-tags. + + * cvs.texinfo (commitinfo): Add comment about using commitinfo to + enforce who has access. + +Wed Jul 24 17:01:41 1996 Larry Jones <larry.jones@sdrc.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (checkout): Refer to "update output" node. + (import): Add new import output node. + (release): Correct release output menu entry (used to be + release options instead). + (update output): Say this is output from checkout as well as + update. + +Mon Sep 16 16:18:38 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Common options): Clarify that CVS uses MM/DD/YY dates. + + * cvs.texinfo (Common options): Add comment about what HEAD means. + +Mon Sep 16 10:52:04 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * cvs.texinfo (Global options): Document global '-T' option. + +Sat Sep 14 10:46:58 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Keeping a checked out copy): New node. + +Fri Sep 13 23:55:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Magic branch numbers): Delete song and dance about + how cvs log can't cope with magic branches because rlog doesn't + know about them; cvs log no longer calls rlog. Delete item about + how you can't specify a symbolic branch to cvs log; that is fixed. + +Wed Sep 11 22:48:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Password authentication server): Add comments + regarding port numbers and troubleshooting. + +Tue Sep 10 10:36:00 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (What is CVS?): Reword text regarding info-cvs, + to avoid overfull hbox. + + * cvs.texinfo (Binary files): Add comment about further issues + with recovering from failure to use -kb. + + * cvs.texinfo (Conflicts example): Describe the "feature" by which + CVS won't check in files with conflicts. + (File status): Expand and revise to document all the possible + statuses from cvs status. Also document "Working revision" and + "Repository revision". Refer to other sections for other aspects + of cvs status. + (status options): Refer to other sections as appropriate. + (update output): Refer user to Conflicts example node. Add + comment regarding purging of .# files. + +Fri Sep 6 11:47:14 1996 Ian Lance Taylor <ian@cygnus.com> + + * cvs.texinfo (Kerberos authenticated): Mention need for + --enable-encryption option in order to use encryption. + (Global options): Likewise, in description of -x option. + +Thu Sep 5 14:31:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Connecting via rsh): Discuss :ext:, :server:, and + CVS_RSH. + (Remote repositories): Mention what default is if no access method + is specified. + (Environment variables): Don't discuss CVS_RSH at length here; + rely on reference to "Connecting via rsh" node. + +Mon Aug 26 15:39:18 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Protocol Notes): When talking about having the + client know the original contents of files, suggest cvs edit as a + solution. + +Thu Aug 22 10:44:40 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Keyword list): Document Name keyword. + + * cvs.texinfo (Tags): Revise comment regarding legal tag names. + +Mon Aug 12 14:58:54 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Password authentication security): Add comment + about how some of this is not pserver-specific. + +Tue Aug 6 16:48:53 1996 Ian Lance Taylor <ian@cygnus.com> + + * cvs.texinfo (log, log options): Update for changes to cvs log + now that it no longer invokes rlog. + +Thu Jul 25 10:10:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Requests): Fix typo (Kerberos-request -> + Kerberos-encrypt). + +Wed Jul 24 18:53:13 1996 Ian Lance Taylor <ian@cygnus.com> + + * cvs.texinfo (Kerberos authenticated): Change the note that the + Kerberos connection is not encrypted. + (Global options): Add documentation for -x. + * cvsclient.texi (Protocol Notes): Remove enhancement note about + Kerberos encryption. + (Requests): Add documentation for Kerberos-encrypt request. + +Thu Jul 18 18:27:40 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Creating a repository): Mention need to be able to + create lock files in the repository. + + * cvsclient.texi (Responses): In F response, make at least a + minimal attempt to define "flush". + + * cvs.texinfo (Wrappers): Document -k. + (From files, Binary files in imports): Say that imports can deal + with binary files and refer to Wrappers node for details. + (Binary files): Likewise for imports and adds. + +Sat Jul 13 18:29:10 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Binary files): Add paragraph concerning the fact + that the keyword expansion mode is not versioned, and why this is + a problem. + +Fri Jul 12 18:55:06 1996 Ian Lance Taylor <ian@cygnus.com> + + * cvsclient.texi (Requests): Document Gzip-stream. + +Thu Jul 11 21:51:45 1996 Ian Lance Taylor <ian@cygnus.com> + + * cvsclient.texi (Responses): Document new "F" response. + +Wed Jul 10 18:46:39 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (log): Don't document "rlog"; it is deprecated. + +Sat Jul 6 22:07:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Environment variables): Document more temp + directory nonsense, this time with "patch". + +Fri Jul 5 23:27:40 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Responses): Add comment regarding "/." ending. + +Fri Sep 13 10:52:09 1996 Greg A. Woods <woods@clapton.seachange.com> + + * cvs.texinfo: don't force afourpaper -- Letter prints much better + on A4 than the other way around, believe you me! + (rdiff options): describe -k and new -K. + (RCS keywords): add description of $Name. + (Using keywords): add description of #ident and example of using + $Name. + - also fixed cross references to Substitution modes in various + places. + (import options): mention that -b 1 imports to the trunk. + +Tue Jul 2 22:40:39 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Sticky tags): Update to reflect change in + "resurrected" message. + +Fri Jun 28 10:48:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Connecting via rsh): Add comment about what we + might be saying about troubleshooting. + +Sun Jun 23 10:07:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Password authentication security): Add comment + regarding anoncvs as practised by OpenBSD. + +Wed Jun 19 15:41:11 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Administrative files): Add xref to Intro + administrative files. + (Intro administrative files): Add comment suggesting future + reorganizations of this material. + (syntax): Add comment regarding this node. + (Getting Notified): Actually document the notify file. It hadn't + really been documented to speak of. + (editinfo,loginfo,rcsfino,cvsignore): Make the index entries + follow the standard "foo (admin file)" format. + +Fri Jun 14 18:14:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (editinfo): Discuss the way editinfo falls down in + the face of -m or -F options to commit, or remote CVS. + +Thu Jun 13 15:08:27 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Watches): Add comment discussing the + fact that using cvs edit instead of chmod is not enforced. + + * cvs.texinfo (Setting up): Add index entry for "init (subcommand)". + (Creating a repository): Move contents of node Setting up here... + (Setting up): ...and remove this node. + (Creating a repository): Don't refer to the INSTALL file (it just + refers back to us!). + + * cvsclient.texi (Responses): Document the fact that the server + should send data only when the client is expecting responses. + +Wed Jun 12 16:04:48 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Entries Lines): Add comment regarding specifying + the meaning of "any other" data, in the conflict field. + (Example): Make it clear that using a separate connection for each + command is not required by the protocol. Add some comments + regarding ways in which the example is out of date or wrong. + +Fri Jun 7 18:02:36 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@cyclic.com> + + * cvs.texinfo (annotate): Document new -r, -D, and -f options. + +Fri Jun 7 16:59:47 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Invoking CVS): Add comment describing why only some + commands are listed here. + (Structure, Environment variables): Don't describe CVS as a + front-end to RCS. + +Tue Jun 4 21:19:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Responses): Document Created and Update-existing. + +Mon Jun 3 17:01:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Responses): Clarify "diff -c" versus "diff -u" + format in Patched response. Don't specify how the client must + implement its patch-applying functionality. + +Sun May 26 17:12:24 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * cvs.texinfo (tag options) Document option "-c". + +Thu May 23 21:11:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Credits): Rewrite section on FAQ to reflect the + fact that FAQ is no longer maintained. + (What is CVS?): Mention comp.software.config-mgmt as well as + info-cvs. Mention the fact that info-cvs-request can be slow in + responding. + (What is CVS?): Rather than say that cvs is not a configuration + mangement system, say specifically what it lacks (change control, + etc.). I added process control (which was sorely lacking from the + list of configuration management functionality), and deleted some + functions such as tape construction which are not provided by the + well-known configuration management systems. + + * cvs.texinfo (checkout options): Add comment regarding + subdirectories (lack of clarity pointed out by ian@cygnus.com). + Add comment about that infernal "short as possible" wording. + + * cvs.texinfo (Global options): Fix error ("diff" -> "log") + (reported by ian@cygnus.com). + Remove footnote "Yes, this really should be fixed, and it's being + worked on"--it isn't clear what "this" is, and I doubt anyone is + working on it. + +Tue May 21 17:22:18 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsclient.texi (Requests): Clarify Directory with "." as local + directory, and that filename for Questionable cannot contain "/". + +Mon May 20 13:15:25 1996 Greg A. Woods <woods@most.weird.com> + + * cvs.texinfo (rdiff): description from main.c:cmd_usage + (rtag): description from main.c:cmd_usage + (status): description from main.c:cmd_usage + (tag): description from main.c:cmd_usage + [all for the sake of consistency] + +Fri May 17 11:42:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo: Add index entries for :local:, etc. + (Password authentication server): Revert erroneous change + regarding the format of CVSROOT/passwd file. + +Thu May 16 17:06:46 1996 Noel Cragg <noel@gargle.rain.org> + + * cvsclient.texi (Notes): Removed paragraphs about various server + invocations which are now described in full in node "Connection + and Authentication." + (Requests): Include a note that "gzip-file-contents" doesn't + follow the upper/lowercase convention and that unknown reqests + always elicit a response, regardless of capitalization. + + * cvs.texinfo (Kerberos authenticated): Removed bogus version + number. + (Repository): explain the ":local:" access method. + +Wed May 15 23:43:04 1996 Noel Cragg <noel@gargle.rain.org> + + * cvsclient.texi (Goals): mention access methods. + (Requests): add note about convention: requests starting with a + captial letter don't have any expected response. Made sure each + request has a "Response expected" note. + + * cvs.texinfo (Remote repositories): add info about access + methods; fix pserver info. + +Tue May 14 08:56:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Environment variables): Try to document somewhat + more accurately where we put temporary files. + + * cvs.texinfo (From files): Say directory tree instead of module + where that is what we mean. Use @var{wdir} and @var{rdir} in the + example instead of using @var{dir} for two different things. + (From files): Say directory tree instead of module + where that is what we mean. + (Binary files): When using cvs admin -kb, one needs an extra + commit step on non-unix systems. + (Binary files in imports): New node. + (Wrappers): Add comment regarding indent example. + (Top): Don't refer to modules when that is not what we mean. + +Fri May 10 09:39:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.texinfo (Sticky tags): Explain what sticky dates and + non-branch sticky tags are good for. + + * cvs.texinfo (Repository): Document that -d overrides CVS/Root. + Wed May 1 15:38:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com> * cvs.texinfo (Tags): Document un-revision of all-uppercase tag diff --git a/contrib/cvs/doc/DIFFUTILS-2.7-BUG b/contrib/cvs/doc/DIFFUTILS-2.7-BUG new file mode 100644 index 0000000..f258ee7 --- /dev/null +++ b/contrib/cvs/doc/DIFFUTILS-2.7-BUG @@ -0,0 +1,263 @@ +The enclosed two messages describe a bug in GNU diff 2.6 and 2.7 which +may cause CVS to perform an erroneous merge. You may wish to use GNU +diff 2.5 or apply the patch supplied by Loren James Rittle below. It +would be nice to add this to the CVS testsuite, but I haven't done so +because probably a lot of people who would like to run the testsuite +are using the buggy diff. + +From: friedman@splode.com (Noah Friedman) +To: bug-gnu-utils@prep.ai.mit.edu +Cc: info-cvs@prep.ai.mit.edu +Subject: diffutils 2.7 -- diff3 merge bug +Date: Tue, 29 Oct 96 17:02:54 CST + +I believe a change first introduced in GNU diff 2.6 causes diff3 sometimes +to produce incorrect merges. + +Since this is a not a bug in CVS itself but can cause commits to CVS +repositories to be incorrect, am warning info-cvs@prep.ai.mit.edu as +well as reporting the bug to bug-gnu-utils@prep.ai.mit.edu. + +I am including a simple test case as well as some sample outputs from +different versions of `diff' and `diff3' in the enclosed shar archive. +In addition, the file DESCRIPTION in that archive describes the problem +more fully. + +If anyone has any advice for how to fix or to work around this bug, I would +appreciate it. Using diff 2.5 seems like the most immediately obvious +solution, but I don't know if it will introduce other problems. +I do not understand the algorithms used by GNU diff well enough to suggest +any patches. + +Unshar and enjoy. ;-) + +begin 666 merge-testcase.shar.gz +M'XL(",&,=C("`VUE<F=E+71E<W1C87-E+G-H87(`[5Q[;]M&$O_[^"FF;E#9 +M@41)?C!GYX&V;MPS<$F+V+FZP.&:%;F26)-<E;NTHC[NL]_,+%^29;USUP-L +M&)9$[CQW]C?#W;$^_ZS="Y.V'CJ?P_4PU("_`O101A&(U!^&=Q+V1ZD*,E\& +MT)O`MV_?XVV19B:,-!R[W0.72!7(CR85O@$SE-`/(ZFAGZH8/Q)'RZD)6B"_ +M$`<IT"J6<''Y]]=-2&6L[B2RD7<RG2!%,H">[*M4,K</A8X-B,)$@N@IXH6W +M$C"3$0[00^;40%60RQL12%`)=$]/O5:WTSH\A:YW=G(,YU?79,*+?AK*(!;) +MEWH4J4"ZOHI?D157*DM]"4&82M^H=`)CH>%#.]-I.U*^B-HZ]=N#)&L'8;_/ +M#FC',AW(EI':^$++7('7'T-MR`CKAW&(SGR:*/,4K0+4/1VGH3&H?9;@?131 +M\AOD>#V2?MA'W2P;G@_R-?@J,2),]!E>C60R,$.(47&P/XF(R7DM_LE?9MXN +M^T%R_#DZ]:"5CO-?F+:M7;QI3=!-VE(\ZQRM0A'CM%D"[_1X%0(5!3*U%,<G +MAPLHWHA;25[FL8?=PV<+QG[S^NK\W>7WUY??O<U9=Q;92Y-\Z)Y87>I&>R>K +MD)'-=:I5A'ESA!T_6X'LOK"3%52LYH?N!ZM,:2%P'FFW>[1HJJ[>OWGSU;L? +M,;2-ROPAM$0,W<,C_#TY/3V%)T]<>_U5.Y!W[23#17/XZHNN$_:!>,!GT.K7 +M";[XPE['JP7M<P8%!WC5_,277O)?1T9:3E\_PX_2'ZK\!1H_?/7N[>7;;\\` +M5RI"DD8$H#5LPAC?BWBD78!SE>@09P@&TO`*%TG0*#CLX0HU(HKH.L'D/S^P +MI$83$46;-.QE!C$T3/CF!08MO$<,"4THM>NZ>X4^_=!)XQE;"P,9%U[6?Q:O +MTNFQ+^N^#*`Q`UZE]ZP_/D(+_%0*MK/"Q!DB'![?XMU[W,B.0AP:TUBH9Z.< +MS9N])]T]^.PEOFGY>W-4TK?A:$0J+39\GU`!1(0&!!-,3C@#^J!1Q$'%+D]; +M*S`T./2`[-4XBPW=_M=-N]V`%R^@<?6WK][]]/J[BP:\6L%.I]V&KS-C5.+^ +M+.Z$<^.,A'\K!A)2C"85NV(TBD(?_:Z2YW@WC$<J-<5-2CON4[H^RGHX#/Q( +M8`JQ#)W?G!N`]E.XI$A,3"A0/.9S>Q<3$28.C.H!QN!^IPF=`XI?^%6F"N\% +M>),^#F4X&!J7.,%3^%%E$&<X,9C_L"1(,'XQ8D44(A5ZS`Q50-D<2X213*-) +M[3;G[MQ0RZQ-+[G:N4K[!W2-U4;'9L@#+Y%U?.$G$YI(GJM(I?`2^-7M1>BM +MY_E]7%BB%\G@^OZX02HFS^ML+C")XEUZ<0/9%UED^!++`_C#^<,I)A(G^4D% +M%1:I.H>GW9-##U/D"E,,X`\Q0T/'\Y:/__WW,B(M[&"1T%\2C7V!X<W0PWKZ +M*DO,R[T/8Q]:/KQ8)O(#@0TO-T[Y\A?8>U+QV;,:%:BVD-59'D\B`DUSCOR: +MX&=I*M&S?*7.F#!A5?RBE/80?"W"$Z+;)9RP'CM$$\MO2S`IC'S$DI6Q!#VU +M#$YHR&J(4C);"BHWN1>Q<KB322@3?,#`<A[K@8SR*>!3#H2EA[FDJ+LX]UP/ +MET^@X6,3)DWKWF;=OP"O?\G".Q'1JC,J)Z*?OE*H7B+'E9.>3]]VR<NP/\7: +MLBU&SG-UB(*0A%XF]B4GI;<Y>6TV^"FP$`U+)*Z&Q,\6(7&Y/E8#8CM\/1SF +M=;PI#+/`"H6YYMX,A8G3+`@CNQV!,#^.;(+"3+A+&+::[!"'<X9;`G%IYR,2 +M_TFKND<`_D0`O+`4KM;%:@B<CU\/@NT2WA2#K<@*A'EK:C,09E:S*(S\MD/A +M8F]K#?PM2':`O*7T[3&W8K49VM:M<KZYO+@XPB5%.U%'_"G_@"N=Z<Z<F[^T +MGNSSN`-HO>:8G4[;,Q$TL[-YPWNO:'(ZR&*</,T8@8[32#E68/"&Q*MCB>$9 +M2"-38LJ[X[CX9.K+$7D!N;".#6U1%@.9/C-L(.",I/65F9*$ZW*$R,];P*3$ +MQ.X>WXET@J)&,@F(!@&`R,)DE!G>9'8=8EW?/20O6">@#P2T6D.,SE]5TJ(M +M=/VRV\%+B_V`L9)F$KW!K*L=QII_Y[/N3K&>=GB=\S*(\;K=.1`S%0P+P:4: +MN1*LE%&Z)J"48BHHX3WKM:"D8#(+(LAI.Q"I;7JO@2,UJAU`25V'[=%DBMMF +M@#)CGF-/6J2AJ+!G-KCHL#Q*\TJ+S2=*&`]YT=.Z9KU(O_QX3#MAXJN4]DEI +M^Q@+$4W#$\ATL2-<GAO!H>OA3%-=(%.L'E@!7V0:N?@JCD.DQ?KK_!]7R&JD +M=$B;T9*O]6C=YW*:H!5<`BZ81)FP/R$YYQ,?ZP3G2O7-6-A`)ZQ`);&DBD$0 +M;E'-IZ&7#5J#)&NQ1E^.4)`K0A=ENS+(L+ASD+/)]ZVG>*`6?,JGHDB-<06* +M9$">"7'Q8I4K42IO<S/:H:%-A[`-ZU["2#2CSP4ELWC[^H<KL(N.(5:8:J\< +M"<Z*DG'/==WO16J04M"$6/\V2Z<BQJK,$!JB'VG_7G[$X,'1)$2+&"-.6T[V +M7-&%]SI#4)X0X/*19P_?Z!@OR;2)GC%\/LE[_U-#(L+^U-TCQ9PWDUIDU".F +M\@[-B/9E(M)0Y=9\90W.S_1X`!V%^G2@1U$PE/XM>@KMH8Q""6<DU8@P_@8X +M+_7#%(5B4:TQ#P0RDC9(^UGBD],XP11!Q'%E)\C.&C$I0VI2\M02]0D*ID3- +MZ8B4&*'?*8H$^G5(]59=#@:(C$<<K<3*BJU)I4.3K\D2R=%MHQ'O<FQ@`/(Q +MKEWA$"C)DV=5I"/A/.KD7:@R'4U:UMB@4B$_8I[1OU%))UY7J@BW/@.KKT9E +M`%:NL)K<DS"DA2(1Z%@ISAVT-*[+4^ZI1-><2:G-F<Q'9]UHC^;'!)R,6;6) +MI56D3`/V$OG:S,Q],=Y!AEQ-W,EH4E<.'CCHXW!.\J<>GR=%0`%==B[L0V7? +M(9EFF$H)=R@3'8(FE.T`/*B.:2=N$:'H&>?^22B;,>=<==HK/`)#-TMNZ8X( +M$%AI*5C4I<=2F>(BCLO0P34TQVQOKMEV]G-0K`";1U2F.?=,\W+3[!HH)#S- +M@X"`5N!LJ0$&:K[2PK3P`'"VEBFM)`I)AQZ_N:XCV/9MF-]*:R'F%YJ3/#ZM +M$V1,_0)<81;^H6'&R2>EK"T)12LF%+EF+*E-8JH:XV":BDM&>RN1P:S@A\H_ +ML"Z$G2HL2`MGLNNI@R1Q"'G4.&'M65H!Q[$PB&^ZOF1Y3"ZO5-A94>$RQ5C5 +M[:(GSJ1C/BWH9G2F`,K"D?5H,\]6ER@O"N6==&*LORS2QP@(.NSE*:F,.4-) +MTU+Q-#)O*S!@_?B>4]W#B,+E6#C.75[M'I[.J79G*Y6%!>_4X)5JWGHMM6;9 +M6Q=65;ZV!6.MTK?&:+;Z)6[;E;_SP&;U,G@.]0[*X7DZ;5\6S^6Z67G\@-G. +MC>M6K4\M',&?-GR\=`X[S:-.T#UU7@#^;K5'1^3K;]+E5`_NTDW?7[A-9X>V +M^763C3JB^\URJ;;J[,=E8I=OU7F=><CRT"0O1)BY1"LAS;SP7!-QY@FO/7-W +MUCW)GL/OWN-WQ]L-_M1JH/7AIR+>(?K4--H=^-29;H<]TS:O`SU+MY^<KM?L +M_M7GO_DB6WPX7(U9>C(QPV[1Z02U7;Z")0<B^8"E<NN,%@E]!-T_!>C.1/=* +MF%NG60MR:XMR0\2MB:Z=EYQL"KBUO>39,Y.3G>"MMU6]YWV2>L_[)/6>M[MZ +MSUM:[WF/]=[_!?1T.P]"C[=)O>=M7.]YV]=[WH[K/>^_4.]YV]1[WJ>H][Q/ +M4>]Y.ZOWO&7UGK=EO7=X^C^O]^SB?02_3<'O?U(O;X>UZY=YWJ9EGK=UF><] +M4.8=/]L09Q>4><<[VM:;W>%?_]EZAL,.'[!G==O=4_8]SML]:L]QP6,WXY^] +MKWR5,O!PP1/HO$E?Z3'T'N%:SZ*SH;OA`^FL$O6R\&3#I](9GO=+PY/=E(;; +M8I;WR3#+^V28Y>T6L[Q'S'K\7YC'5NP5@-[;%.B]K8#>VPW0>PN`?OW_F7F` +MY\[_>R;_1_<U@#VGV`&0%[*W!^Z2TV9`73/)N;E(57P&<[YV`_;?*C&$B_S. +M@7.MSA;W)3KG_ADNFKYJ^7?W;UYEO9^E;\ZF6G>>T09!U;(I28#S3HZB28O$ +MS5'+^08!_@RN,]F$PU/XSC?\92+\-2)'9]UC:'6\3@?VSZ^N#YP+'S7Z=UL: +MOQUCD+>1NZ[V+O"QE1R8MP-JN_IIYP*?9LOV$VI+LDUZMK\+@:/HL<J_+J!H +M=,F;1'-SRAY%QZ8*(IEM:N)6F:LPX3:CX@M>J$52D!^(/W69AD;+J,^-CSXF +M(]NL-]V)ZBSN1$4T&HN4^QD?FAYJPBR:3XE96C;`DR;$<86.U)B$1EE@TX/& +M+![)6AMFK;^5VRJUX`&V.]1^)XU3]D^536T$9!^XD913R0?;N%\T86&BBA1% +M/W\92_YE-B[U'XD@"*F>:%8=4/66&J87IOPFG4!J/PU[>0-4WD[KQ(2D_2RR +MS7N7U&DY48GDUD-\BT+NJ$F,LN-0C<E/_?`C=0_CN[%*;Y$]I40[O=Q3>8G7 +MLRAPL.+!R:%J!2<8,^-[;;_+P79VX8*6,07CK:TI8D6A%\<R(`JL.52/&R\= +MK:+,6DGQ<0F!2AH&;A-4!D$M-+:7K0S:O$<N-P_CCR@XY%!+=+@IFAE%-,!@ +M,D-4(M/5UPNQ?CR),E'98,C?%I0-,)2-0_X8V28R<M;[Q$X)M9@E/ZL)VOB\ +M=;`\8YX<S<F8=<A:F"'+@2MEQ`)(U\R`A9`JX]GO5UDKY>5,9E,<<5J2XS!E +-&.@X_P&I*Y$W($H``"'+ +` +end + +Date: Wed, 30 Oct 96 14:54:13 CST +From: Loren James Rittle <rittle@comm.mot.com> +To: friedman@splode.com +Cc: bug-gnu-utils@prep.ai.mit.edu, info-cvs@prep.ai.mit.edu +Subject: Re: diffutils 2.7 -- diff3 merge bug + +Noah, + +I have seen the problem you discuss in your e-mail, however I fail to +see how this situation is as critical as might be implied since it can +never arise without at least some user involvement (an update that +caused a merge is never automatically followed by a commit --- the +user has a chance to inspect the merged file). However, I will agree +that I don't always look very closely at non-conflicted merges before +checking them back in. + +You didn't give the exact CVS commands used to create a lossage, +but I added the following to your Makefile, to help me see the problem +in a CVS usage context: + +t-older: testcase-older + cp testcase-older t-older + +t-yours: testcase-yours + cp testcase-yours t-yours + +t-mine: testcase-mine + cp testcase-mine t-mine + +# Assume cvs-1.9 +cvs-test: t-older t-yours t-mine + rm -rf /tmp/cvs-test-root x x2 + cvs -d /tmp/cvs-test-root init + mkdir x + cp t-older x/testcase + cd x; cvs -d /tmp/cvs-test-root import -m '' x X X1 + rm -rf x + cvs -d /tmp/cvs-test-root co x + cvs -d /tmp/cvs-test-root co -d x2 x + cp t-yours x/testcase + cp t-mine x2/testcase + cd x; cvs ci -m '' + -cd x2; cvs ci -m '' + cd x2; cvs update + cat x2/testcase # at this point, user may commit blindly + +It looks like whomever added shift_boundaries() in analyze.c, which +seems to be the source of the diff3 induced mischief, already provided +a means to disable the boundary shifting optimization (at least with +a recompile). + +Here is the patch I applied to diff to disable this (currently) +overaggressive optimization: + +[ rittle@supra ]; diff -c analyze.c-old analyze.c +*** analyze.c-old Wed Oct 30 14:10:27 1996 +--- analyze.c Wed Oct 30 13:48:57 1996 +*************** +*** 616,622 **** + but usually it is cleaner to consider the following identical line + to be the "change". */ + +! int inhibit; + + static void + shift_boundaries (filevec) +--- 616,622 ---- + but usually it is cleaner to consider the following identical line + to be the "change". */ + +! int inhibit = 1; + + static void + shift_boundaries (filevec) + +Now, diff-2.7 with the above patch produces: + +[ rittle@supra ]; make diff-mine-yours 'DIFF=/usr/src/diffutils-2.7/diff' +/usr/src/diffutils-2.7/diff -a --horizon-lines=11 -- testcase-mine testcase-yours; true +16,18c16,18 +< // _titleColor = Color.black; +< // _disabledTitleColor = Color.gray; +< // _titleFont = Font.defaultFont (); +--- +> _titleColor = Color.black; +> _disabledTitleColor = Color.gray; +> _titleFont = Font.defaultFont (); +20,30d19 +< +< /* Convenience constructor for instantiating a Button with +< * bounds x, y, width, and height. Equivalent to +< * foo = new Button (); +< * foo.init (x, y, width, height); +< */ +< public Button (int x, int y, int width, int height) +< { +< this (); +< init (x, y, width, height); +< } + +Whereas, stock diff-2.7 produces: + +[ rittle@supra ]; make diff-mine-yours +diff -a --horizon-lines=11 -- testcase-mine testcase-yours; true +16,29c16,18 +< // _titleColor = Color.black; +< // _disabledTitleColor = Color.gray; +< // _titleFont = Font.defaultFont (); +< } +< +< /* Convenience constructor for instantiating a Button with +< * bounds x, y, width, and height. Equivalent to +< * foo = new Button (); +< * foo.init (x, y, width, height); +< */ +< public Button (int x, int y, int width, int height) +< { +< this (); +< init (x, y, width, height); +--- +> _titleColor = Color.black; +> _disabledTitleColor = Color.gray; +> _titleFont = Font.defaultFont (); + +A better solution might be to disable the boundary shifting code +unless explicitly turned on via command line argument. That way +programs, like diff3, expecting unoptimized diff regions will work +correctly, yet users can get smaller diffs, if desired. The problem +is that diff3 doesn't properly track changes once they have been +optimized. + +BTW, I never did like the look of the `optimized diff regions', so I +consider this a good change for other reasons... :-) + +Enjoy! + +Regards, +Loren +-- +Loren J. Rittle (rittle@comm.mot.com) PGP KeyIDs: 1024/B98B3249 2048/ADCE34A5 +Systems Technology Research (IL02/2240) FP1024:6810D8AB3029874DD7065BC52067EAFD +Motorola, Inc. FP2048:FDC0292446937F2A240BC07D42763672 +(847) 576-7794 Call for verification of fingerprints. diff --git a/contrib/cvs/doc/Makefile.in b/contrib/cvs/doc/Makefile.in index 11c7051..29eb565 100644 --- a/contrib/cvs/doc/Makefile.in +++ b/contrib/cvs/doc/Makefile.in @@ -12,12 +12,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# $CVSid: @(#)Makefile.in 1.8 94/10/22 $ - SHELL = /bin/sh srcdir = @srcdir@ @@ -34,6 +28,7 @@ INSTALL_DATA = @INSTALL_DATA@ DISTFILES = \ .cvsignore ChangeLog ChangeLog.fsf Makefile.in \ + RCSFILES \ cvs-paper.ms cvs-paper.ps \ cvs.texinfo \ cvsclient.texi @@ -99,10 +94,12 @@ cvsclient.txt: cvsclient.texi CVSvn.texi # it, and builds it in a seperate build dir, then *.info* are in srcdir. # If the user builds *.info (e.g. after editing *.texi), then *.info* are # in the build dir. -install-info: info - test -f cvs.info || cd $(srcdir); \ - for i in *.info* ; do \ - $(INSTALL_DATA) $$i $(infodir)/$$i ; \ +# (Note: don't solve this problem with "cd"; INSTALL_DATA might be a +# relative path to install-sh). +install-info: info installdirs + if test -f cvs.info ; then docdir=.; else docdir=$(srcdir);fi; \ + for i in $$docdir/*.info* ; do \ + $(INSTALL_DATA) $$i $(infodir)/`basename $$i` ; \ done installdirs: @@ -130,10 +127,7 @@ cvsclient.dvi cvsclient.aux: cvsclient.texi CVSvn.texi ln -s $(srcdir)/CVSvn.texi . || \ ln $(srcdir)/CVSvn.texi . || \ cp $(srcdir)/CVSvn.texi . ; else true; fi - $(SET_TEXINPUTS) $(TEX) cvsclient.texi - $(SET_TEXINPUTS) $(TEX) cvsclient.texi - $(TEXINDEX) cvsclient.?? - $(SET_TEXINPUTS) $(TEX) cvsclient.texi + $(TEXI2DVI) $(srcdir)/cvsclient.texi rm -f cvsclient.?? cvsclient.log cvsclient.toc cvsclient.??s cvs.ps: cvs.dvi diff --git a/contrib/cvs/doc/RCSFILES b/contrib/cvs/doc/RCSFILES new file mode 100644 index 0000000..41d131b --- /dev/null +++ b/contrib/cvs/doc/RCSFILES @@ -0,0 +1,165 @@ +It would be nice for the RCS file format (which is implemented by a +great many tools, both free and non-free, both by calling GNU RCS and +by reimplementing access to RCS files) were documented in some +standard separate from any one tool. But as far as I know no such +standard exists. Hence this file. + +The place to start is the rcsfile.5 manpage in the GNU RCS 5.7 +distribution. Then look at the diff at the end of this file (which +contains a few fixes and clarifications to that manpage). + +If you are interested in MKS RCS, src/ci.c in GNU RCS 5.7 has a +comment about their date format. However, as far as we know there +isn't really any document describing MKS's changes to the RCS file +format. + +The rcsfile.5 manpage does not document what goes in the "text" field +for each revision. The answer is that the head revision contains the +contents of that revision and every other revision contain a bunch of +edits to produce that revision ("a" and "d" lines). The GNU diff +manual (the version I looked at was for GNU diff 2.4) documents this +format somewhat (as the "RCS output format"), but the presentation is +a bit confusing as it is all tangled up with the documentation of +several other output formats. If you just want some source code to +look at, the part of CVS which applies these is RCS_deltas in +src/rcs.c. + +The first time I read rcsfile.5 I didn't really notice the part about +the order of the revisions. This order _is_ important and CVS relies +on it. It is documented but it would be clearer if the example in +rcsfile.5 also showed the order of the revisions (and the "next" and +"branch" fields and anything else where it would be useful to have an +example of how a revision tree is represented in an RCS file). + +There is one case where CVS uses CVS-specific, non-compatible changes +to the RCS file format, and this is magic branches. See cvs.texinfo +for more information on them. CVS also sets the RCS state to "dead" +to indicate that a file does not exist in a given revision (this is +stored just as any other RCS state is). + +The rules regarding keyword expansion are not documented along with +the rest of the RCS file format; they are documented in the co(1) +manpage in the RCS 5.7 distribution. See also the "Keyword +substitution" chapter of cvs.texinfo. The co(1) manpage refers to +special behavior if the log prefix for the $Log keyword is /* or (*. +RCS 5.7 produces a warning whenever it behaves that way, and current +versions of CVS do not handle this case in a special way (CVS 1.9 and +earlier invoke RCS to perform keyword expansion). + +Note that the "comment {string};" syntax from rcsfile.5 specifies a +comment leader, which affects expansion of the $Log keyword for old +versions of RCS. The comment leader is not used by RCS 5.7 or current +versions of CVS. + +Both RCS 5.7 and current versions of CVS handle the $Log keyword in a +different way if the log message starts with "checked in with -k by ". +I don't think this behavior is documented anywhere. + +One common concern about the RCS file format is the fact that to get +the head of a branch, one must apply deltas from the head of the trunk +to the branchpoint, and then from the branchpoint to the head of the +branch. While more detailed analyses might be worth doing, we will +note: + + * The performance bottleneck for CVS generally is figuring out which + files to operate on and that sort of thing, not applying deltas. + + * Here is one quick test (probably not a very good test; a better test + would use a normally sized file (say 50-200K) instead of a small one): + + I just did a quick test with a small file (on a Sun Ultra 1/170E + running Solaris 5.5.1), with 1000 revisions on the main branch and + 1000 revisions on branch that forked at the root (i.e., RCS revisions + 1.1, 1.2, ..., 1.1000, and branch revisions 1.1.1.1, 1.1.1.2, ..., + 1.1.1.1000). It took about 0.15 seconds real time to check in the + first revision, and about 0.6 seconds to check in and 0.3 seconds to + retrieve revision 1.1.1.1000 (the worst case). + + * Any attempt to "fix" this problem should be careful not to interfere + with other features, such as lightweight creation of branches + (particularly using CVS magic branches). + +Diff follows: + +(Note that in the following diff the old value for the Id keyword was: + Id: rcsfile.5in,v 5.6 1995/06/05 08:28:35 eggert Exp +and the new one was: + Id: rcsfile.5in,v 5.7 1996/12/09 17:31:44 eggert Exp +but since this file itself might be subject to keyword expansion I +haven't included a diff for that fact). + +=================================================================== +RCS file: RCS/rcsfile.5in,v +retrieving revision 5.6 +retrieving revision 5.7 +diff -u -r5.6 -r5.7 +--- rcsfile.5in 1995/06/05 08:28:35 5.6 ++++ rcsfile.5in 1996/12/09 17:31:44 5.7 +@@ -85,7 +85,8 @@ + .LP + \f2sym\fP ::= {\f2digit\fP}* \f2idchar\fP {\f2idchar\fP | \f2digit\fP}* + .LP +-\f2idchar\fP ::= any visible graphic character except \f2special\fP ++\f2idchar\fP ::= any visible graphic character, ++ except \f2digit\fP or \f2special\fP + .LP + \f2special\fP ::= \f3$\fP | \f3,\fP | \f3.\fP | \f3:\fP | \f3;\fP | \f3@\fP + .LP +@@ -119,12 +120,23 @@ + the minute (00\-59), + and + .I ss +-the second (00\-60). ++the second (00\-59). ++If + .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. ++contains exactly two digits, ++they are the last two digits of a year from 1900 through 1999; ++otherwise, ++.I Y ++contains all the digits of the year. ++Dates use the Gregorian calendar. ++Times use UTC, except that for portability's sake leap seconds are not allowed; ++implementations that support leap seconds should output ++.B 59 ++for ++.I ss ++during an inserted leap second, and should accept ++.B 59 ++for a deleted leap second. + .PP + The + .I newphrase +@@ -144,16 +156,23 @@ + field in order of decreasing numbers. + The + .B head +-field in the +-.I admin +-node points to the head of that sequence (i.e., contains ++field points to the head of that sequence (i.e., contains + the highest pair). + The + .B branch +-node in the admin node indicates the default ++field indicates the default + branch (or revision) for most \*r operations. + If empty, the default + branch is the highest branch on the trunk. ++The ++.B symbols ++field associates symbolic names with revisions. ++For example, if the file contains ++.B "symbols rr:1.1;" ++then ++.B rr ++is a name for revision ++.BR 1.1 . + .PP + All + .I delta + diff --git a/contrib/cvs/doc/cvs.texinfo b/contrib/cvs/doc/cvs.texinfo index 29bc5f3..6330984 100644 --- a/contrib/cvs/doc/cvs.texinfo +++ b/contrib/cvs/doc/cvs.texinfo @@ -16,11 +16,29 @@ @comment MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @comment GNU General Public License for more details. -@comment You should have received a copy of the GNU General Public License -@comment along with CVS; see the file COPYING. If not, write to -@comment the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +@c See ../README for A4 vs. US letter size. +@c When we provided A4 postscript, and people tried to +@c print it on US letter, the usual complaint was that the +@c page numbers would get cut off. +@c If one prints US letter on A4, reportedly there is +@c some extra space at the top and/or bottom, and the side +@c margins are a bit narrow, but no text is lost. +@c +@c See +@c http://www.ft.uni-erlangen.de/~mskuhn/iso-paper.html +@c for more on paper sizes. Insuring that margins are +@c big enough to print on either A4 or US letter does +@c indeed seem to be the usual approach (according to +@c an internet draft by Jacob Palme). + +@c This document seems to get overfull hboxes with some +@c frequency (probably because the tendency is to +@c sanity-check it with "make info" and run TeX less +@c often). The big ugly boxes just seem to add insult +@c to injury, and I'm not aware of them helping to fix +@c the overfull hboxes at all. +@finalout -@afourpaper @setfilename cvs.info @include CVSvn.texi @settitle CVS---Concurrent Versions System @@ -120,27 +138,36 @@ This info manual describes how to use and administer @sc{cvs} version @value{CVSVN}. @end ifinfo +@c This menu is pretty long. Not sure how easily that +@c can be fixed; seems like "Adding files", "Removing +@c files", "Removing directories", "Moving files", +@c and "Moving directories" all go together (into +@c "Adding, removing, and renaming"?). Other than that +@c no brilliant ideas for a fix... @menu * Preface:: About this manual * What is CVS?:: What is CVS? -* Basic concepts:: Basic concepts of revision management * A sample session:: A tour of basic CVS usage * Repository:: Where all your sources are stored * Starting a new project:: Starting a project with CVS * Multiple developers:: How CVS helps a group of developers -* Branches:: Parallel development explained +* Revisions and branches:: Numeric, symbolic, and branch revisions * Merging:: How to move changes between branches * Recursive behavior:: CVS descends directories -* Adding files:: Adding files to a module -* Removing files:: Removing files from a module +* Adding files:: Adding files +* Removing files:: Removing files +* Removing directories:: Removing directories * Tracking sources:: Tracking third-party sources * Moving files:: Moving and renaming files * Moving directories:: Moving and renaming directories * History browsing:: Viewing the history of files in various ways * Keyword substitution:: CVS can include the revision inside the file * Binary files:: CVS can handle binary files +* Builds:: Issues related to CVS and builds +* Compatibility:: Upgrading CVS versions * Revision management:: Policy questions for revision management -* Invoking CVS:: Reference manual for CVS commands +* CVS commands:: CVS commands share some things +* Invoking CVS:: Quick reference to CVS commands * Administrative files:: Reference manual for the Administrative files * Environment variables:: All environment variables which affect CVS * Troubleshooting:: Some tips when nothing works @@ -187,34 +214,6 @@ every @sc{cvs} command is gathered together. There is also an extensive index, and a lot of cross references. @end itemize -@cindex Signum Support -@cindex Support, getting CVS support -This manual was contributed by Signum Support AB in -Sweden. Signum is yet another in the growing list of -companies that support free software. You are free to -copy both this manual and the @sc{cvs} program. -@xref{Copying}, for the details. Signum Support offers -@c -- Check this reference! It has been bogus in the past. -support contracts and binary distribution for many -programs, such as @sc{cvs}, @sc{gnu} Emacs, the -@sc{gnu} C compiler and others. Write to us for -more information. - -@example -Signum Support AB -Box 2044 -S-580 02 Linkoping -Sweden - -Email: info@@signum.se -Phone: +46 (0)13 - 21 46 00 -Fax: +46 (0)13 - 21 47 00 -@end example - -Another company selling support for @sc{cvs} is Cyclic -Software, web: @code{http://www.cyclic.com/}, email: -@code{info@@cyclic.com}. - @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @menu * Checklist:: @@ -243,12 +242,12 @@ flag (release 1.15 and later are OK). You must also configure both @sc{rcs} and @sc{cvs} to handle binary files when you install them. -Keword substitution can be a source of trouble with +Keyword substitution can be a source of trouble with binary files. @xref{Keyword substitution}, for solutions. @item The @code{admin} command -Uncareful use of the @code{admin} command can cause +Careless use of the @code{admin} command can cause @sc{cvs} to cease working. @xref{admin}, before trying to use it. @end table @@ -259,10 +258,10 @@ to use it. @cindex Contributors (manual) @cindex Credits (manual) -Roland Pesch, Cygnus Support <@t{pesch@@cygnus.com}> +Roland Pesch, then of Cygnus Support <@t{roland@@wrs.com}> wrote the manual pages which were distributed with -@sc{cvs} 1.3. Appendix A and B contain much text that -was extracted from them. He also read an early draft +@sc{cvs} 1.3. Much of their text was copied into this +manual. He also read an early draft of this manual and contributed many ideas and corrections. @@ -274,15 +273,16 @@ David G. Grubbs <@t{dgg@@think.com}>. Some text has been extracted from the man pages for @sc{rcs}. -The @sc{cvs} @sc{faq} (@pxref{What is CVS?}) by David -G. Grubbs has been used as a check-list to make sure -that this manual is as complete as possible. (This -manual does however not include all of the material in -the @sc{faq}). The @sc{faq} contains a lot of useful -information. +The @sc{cvs} @sc{faq} by David G. Grubbs has provided +useful material. The @sc{faq} is no longer maintained, +however, and this manual is about the closest thing there +is to a successor (with respect to documenting how to +use @sc{cvs}, at least). In addition, the following persons have helped by telling me about mistakes I've made: + +@display Roxanne Brunskill <@t{rbrunski@@datap.ca}>, Kathy Dyer <@t{dyer@@phoenix.ocf.llnl.gov}>, Karl Pingle <@t{pingle@@acuson.com}>, @@ -290,52 +290,122 @@ Thomas A Peterson <@t{tap@@src.honeywell.com}>, Inge Wallin <@t{ingwa@@signum.se}>, Dirk Koschuetzki <@t{koschuet@@fmi.uni-passau.de}> and Michael Brown <@t{brown@@wi.extrel.com}>. +@end display + +The list of contributors here is not comprehensive; for a more +complete list of who has contributed to this manual see +the file @file{doc/ChangeLog} in the @sc{cvs} source +distribution. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node BUGS @unnumberedsec BUGS -@cindex Bugs, known in this manual -@cindex Known bugs in this manual -This manual is known to have room for improvement. -Here is a list of known deficiencies: - +@cindex Bugs in this manual or CVS +Neither @sc{cvs} nor this manual is perfect, and they +probably never will be. If you are having trouble +using @sc{cvs}, or think you have found a bug, there +are a number of things you can do about it. Note that +if the manual is unclear, that can be considered a bug +in the manual, so these problems are often worth doing +something about as well as problems with @sc{cvs} itself. + +@cindex Reporting bugs +@cindex Bugs, reporting +@cindex Errors, reporting @itemize @bullet @item -In the examples, the output from @sc{cvs} is sometimes -displayed, sometimes not. +If you want someone to help you and fix bugs that you +report, there are companies which will do that for a +fee. Two such companies are: -@item -The input that you are supposed to type in the examples -should have a different font than the output from the -computer. +@cindex Signum Support +@cindex Cyclic Software +@cindex Support, getting CVS support +@example +Signum Support AB +Box 2044 +S-580 02 Linkoping +Sweden +Email: info@@signum.se +Phone: +46 (0)13 - 21 46 00 +Fax: +46 (0)13 - 21 47 00 +http://www.signum.se/ + +Cyclic Software +United States of America +http://www.cyclic.com/ +info@@cyclic.com +@end example @item -This manual should be clearer about what file -permissions you should set up in the repository, and -about setuid/setgid. +If you got @sc{cvs} through a distributor, such as an +operating system vendor or a vendor of freeware +@sc{cd-rom}s, you may wish to see whether the +distributor provides support. Often, they will provide +no support or minimal support, but this may vary from +distributor to distributor. @item -Some of the chapters are not yet complete. They are -noted by comments in the @file{cvs.texinfo} file. +If you have the skills and time to do so, you may wish +to fix the bug yourself. If you wish to submit your +fix for inclusion in future releases of @sc{cvs}, see +the file @sc{hacking} in the @sc{cvs} source +distribution. It contains much more information on the +process of submitting fixes. @item -@cindex Reporting bugs (manual) -@cindex Bugs, reporting (manual) -@cindex Errors, reporting (manual) -This list is not complete. If you notice any error, -omission, or something that is unclear, please send -mail to @t{bug-cvs@@prep.ai.mit.edu}. -@end itemize +There may be resources on the net which can help. Two +good places to start are: + +@example +http://www.cyclic.com + @r{particularly the Unsupported Resources page} +http://www.loria.fr/~molli/cvs-index.html +@end example -I hope that you will find this manual useful, despite -the above-mentioned shortcomings. +If you are so inspired, increasing the information +available on the net is likely to be appreciated. For +example, before the standard @sc{cvs} distribution +worked on Windows 95, there was a web page with some +explanation and patches for running @sc{cvs} on Windows +95, and various people helped out by mentioning this +page on mailing lists or newsgroups when the subject +came up. -@flushright +@item +It is also possible to report bugs to @code{bug-cvs}. +Note that someone may or may not want to do anything +with your bug report---if you need a solution consider +one of the options mentioned above. People probably do +want to hear about bugs which are particularly severe +in consequences and/or easy to fix, however. You can +also increase your odds by being as clear as possible +about the exact nature of the bug and any other +relevant information. The way to report bugs is to +send email to @code{bug-cvs@@prep.ai.mit.edu}. Note +that submissions to @code{bug-cvs} may be distributed +under the terms of the @sc{gnu} Public License, so if +you don't like this, don't submit them. There is +usually no justification for sending mail directly to +one of the @sc{cvs} maintainers rather than to +@code{bug-cvs}; those maintainers who want to hear +about such bug reports read @code{bug-cvs}. Also note +that sending a bug report to other mailing lists or +newsgroups is @emph{not} a substitute for sending it to +@code{bug-cvs}. It is fine to discuss @sc{cvs} bugs on +whatever forum you prefer, but there are not +necessarily any maintainers reading bug reports sent +anywhere except @code{bug-cvs}. +@end itemize -Linkoping, October 1993 -Per Cederqvist -@end flushright +@cindex Known bugs in this manual or CVS +People often ask if there is a list of known bugs or +whether a particular bug is a known one. The file +@sc{bugs} in the @sc{cvs} source distribution is one +list of known bugs, but it doesn't necessarily try to +be comprehensive. Perhaps there will never be a +comprehensive, detailed list of known bugs. @c --------------------------------------------------------------------- @node What is CVS? @@ -383,7 +453,8 @@ the work when each developer is done. @cindex Credits (CVS program) @cindex Contributors (CVS program) @sc{cvs} started out as a bunch of shell scripts written by -Dick Grune, posted to @code{comp.sources.unix} in the volume 6 +Dick Grune, posted to the newsgroup +@code{comp.sources.unix} in the volume 6 release of December, 1986. While no actual code from these shell scripts is present in the current version of @sc{cvs} much of the @sc{cvs} conflict resolution algorithms @@ -394,21 +465,56 @@ Jeff Polk later helped Brian with the design of the @sc{cvs} module and vendor branch support. @cindex Source, getting CVS source -You can get @sc{cvs} via anonymous ftp from a number of -sites, for instance @t{prep.ai.mit.edu} in -@file{pub/gnu}. +You can get @sc{cvs} via anonymous @sc{ftp} from a +number of sites; for example see +@example +http://www.gnu.ai.mit.edu/order/ftp.html +@end example +for a list of the @sc{gnu} @sc{ftp} sites. +@c We could also be pointing to other resources like +@c the cyclic getting.html, Pascal Molli's page, etc., +@c and probably should, when someone gets around to +@c figuring out which pages are stable enough that we +@c should cite them, which ones are best to point +@c people to (supported? binary? source? zero-cost? +@c buying CD-ROMs? etc.), etc. @cindex Mailing list @cindex List, mailing list -There is a mailing list for @sc{cvs} where bug reports -can be sent, questions can be asked, an FAQ is posted, -and discussion about future enhancements to @sc{cvs} -take place. To submit a message to the list, write to -<@t{info-cvs@@prep.ai.mit.edu}>. To subscribe or -unsubscribe, write to -<@t{info-cvs-request@@prep.ai.mit.edu}>. Please be -specific about your email address. - +@cindex Newsgroups +@c Be careful in editing this--it is worded so that +@c the long -request address is in the middle of a +@c line, thus avoiding overfull hboxes. +There is a mailing list, known as @w{@code{info-cvs}}, +devoted to @sc{cvs}. To subscribe or +unsubscribe +@c could add "to the mailing list," +send a message to +@c or "write to" +@w{@code{info-cvs-request@@prep.ai.mit.edu}}. Please +be specific about your email address. As of May 1996, +subscription requests are handled by a busy human +being, so you cannot expect to be added or removed +immediately. If you prefer a usenet group, the right +group is @code{comp.software.config-mgmt} which is for +@sc{cvs} discussions (along with other configuration +management systems). In the future, it might be +possible to create a +@code{comp.software.config-mgmt.cvs}, but probably only +if there is sufficient @sc{cvs} traffic on +@code{comp.software.config-mgmt}. +@c Other random data is that past attempts to create a +@c gnu.* group have failed (the relevant authorities +@c say they'll do it, but don't), and that tale was very +@c skeptical of comp.software.config-mgmt.cvs when the +@c subject came up around 1995 or so (for one +@c thing, because creating it would be a "reorg" which +@c would need to take a more comprehensive look at the +@c whole comp.software.config-mgmt.* hierarchy). + +You can also subscribe to the bug-cvs mailing list, +described in more detail in @ref{BUGS}. To subscribe +send mail to bug-cvs-request@@prep.ai.mit.edu. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @unnumberedsec CVS is not@dots{} @@ -433,7 +539,7 @@ checked out working directories. If you write your @file{Makefile}s or scripts in every directory so they have to know the relative positions of everything else, you wind up requiring the entire repository to be -checked out. That's simply bad planning. +checked out. If you modularize your work, and construct a build system that will share files (via links, mounts, @@ -442,14 +548,22 @@ arrange your disk usage however you like. But you have to remember that @emph{any} such system is a lot of work to construct and maintain. @sc{cvs} does -not address the issues involved. You must use your -brain and a collection of other tools to provide a -build scheme to match your plans. +not address the issues involved. Of course, you should place the tools created to support such a build system (scripts, @file{Makefile}s, etc) under @sc{cvs}. +Figuring out what files need to be rebuilt when +something changes is, again, something to be handled +outside the scope of @sc{cvs}. One traditional +approach is to use @code{make} for building, and use +some automated tool for generating the dependencies which +@code{make} uses. + +See @ref{Builds}, for more information on doing builds +in conjunction with @sc{cvs}. + @item @sc{cvs} is not a substitute for management. Your managers and project leaders are expected to talk @@ -489,175 +603,64 @@ Acquire the habit of reading specs and talking to your peers. -@item @sc{cvs} is not a configuration management system. - -@sc{cvs} is a source control system. The phrase -``configuration management'' is a marketing term, not -an industry-recognized set of functions. - -A true ``configuration management system'' would contain -elements of the following: - -@itemize @bullet -@item Source control. -@item Dependency tracking. -@item Build systems (i.e. What to build and how to find -things during a build. What is shared? What is local?) -@item Bug tracking. -@item Automated Testing procedures. -@item Release Engineering documentation and procedures. -@item Tape Construction. -@item Customer Installation. -@item A way for users to run different versions of the same -software on the same host at the same time. -@end itemize - -@sc{cvs} provides only the first. +@item @sc{cvs} does not have change control + +Change control refers to a number of things. First of +all it can mean @dfn{bug-tracking}, that is being able +to keep a database of reported bugs and the status of +each one (is it fixed? in what release? has the bug +submitter agreed that it is fixed?). For interfacing +@sc{cvs} to an external bug-tracking system, see the +@file{rcsinfo} and @file{verifymsg} files +(@pxref{Administrative files}). + +Another aspect of change control is keeping track of +the fact that changes to several files were in fact +changed together as one logical change. If you check +in several files in a single @code{cvs commit} +operation, @sc{cvs} then forgets that those files were +checked in together, and the fact that they have the +same log message is the only thing tying them +together. Keeping a @sc{gnu} style @file{ChangeLog} +can help somewhat. +@c FIXME: should have an xref to a section which talks +@c more about keeping ChangeLog's with CVS, but that +@c section hasn't been written yet. + +Another aspect of change control, in some systems, is +the ability to keep track of the status of each +change. Some changes have been written by a developer, +others have been reviewed by a second developer, and so +on. Generally, the way to do this with @sc{cvs} is to +generate a diff (using @code{cvs diff} or @code{diff}) +and email it to someone who can then apply it using the +@code{patch} utility. This is very flexible, but +depends on mechanisms outside @sc{cvs} to make sure +nothing falls through the cracks. + +@item @sc{cvs} is not an automated testing program + +It should be possible to enforce mandatory use of a +testsuite using the @code{commitinfo} file. I haven't +heard a lot about projects trying to do that or whether +there are subtle gotchas, however. + +@item @sc{cvs} does not have a builtin process model + +Some systems provide ways to ensure that changes or +releases go through various steps, with various +approvals as needed. Generally, one can accomplish +this with @sc{cvs} but it might be a little more work. +In some cases you'll want to use the @file{commitinfo}, +@file{loginfo}, @file{rcsinfo}, or @file{verifymsg} +files, to require that certain steps be performed +before cvs will allow a checkin. Also consider whether +features such as branches and tags can be used to +perform tasks such as doing work in a development tree +and then merging certain changes over to a stable tree +only once they have been proven. @end table -This section is taken from release 2.3 of the @sc{cvs} -@sc{faq}. - -@c --------------------------------------------------------------------- -@node Basic concepts -@chapter Basic concepts -@cindex Modules (intro) -@cindex Repository (intro) - -@sc{cvs} stores all files in a centralized -@dfn{repository}: a directory (such as -@file{/usr/local/cvsroot} or -@file{user@@remotehost:/usr/local/cvsroot}) which is -populated with a hierarchy of files and directories. -(@pxref{Remote repositories} for information about -keeping the repository on a remote machine.) - -Normally, you never access any of the files in the -repository directly. Instead, you use @sc{cvs} -commands to get your own copy of the files, and then -work on that copy. When you've finished a set of -changes, you check (or @dfn{commit}) them back into the -repository. - -The files in the repository are organized in -@dfn{modules}. Each module is made up of one or more -files, and can include files from several directories. -A typical usage is to define one module per project. - -@menu -* Revision numbers:: The meaning of a revision number -* Versions revisions releases:: Terminology used in this manual -@end menu - -@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@node Revision numbers -@section Revision numbers -@cindex Revision numbers -@cindex Revision tree -@cindex Linear development -@cindex Number, revision- -@cindex Decimal revision number -@cindex Main trunk (intro) -@cindex Branch number -@cindex Number, branch - -Each version of a file has a unique @dfn{revision -number}. Revision numbers look like @samp{1.1}, -@samp{1.2}, @samp{1.3.2.2} or even @samp{1.3.2.2.4.5}. -A revision number always has an even number of -period-separated decimal integers. By default revision -1.1 is the first revision of a file. Each successive -revision is given a new number by increasing the -rightmost number by one. The following figure displays -a few revisions, with newer revisions to the right. - -@example - +-----+ +-----+ +-----+ +-----+ +-----+ - ! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! - +-----+ +-----+ +-----+ +-----+ +-----+ -@end example - -@sc{cvs} is not limited to linear development. The -@dfn{revision tree} can be split into @dfn{branches}, -where each branch is a self-maintained line of -development. Changes made on one branch can easily be -moved back to the main trunk. - -Each branch has a @dfn{branch number}, consisting of an -odd number of period-separated decimal integers. The -branch number is created by appending an integer to the -revision number where the corresponding branch forked -off. Having branch numbers allows more than one branch -to be forked off from a certain revision. - -@need 3500 -All revisions on a branch have revision numbers formed -by appending an ordinal number to the branch number. -The following figure illustrates branching with an -example. - -@example -@group - +-------------+ - Branch 1.2.2.3.2 -> ! 1.2.2.3.2.1 ! - / +-------------+ - / - / - +---------+ +---------+ +---------+ +---------+ -Branch 1.2.2 -> _! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 !----! 1.2.2.4 ! - / +---------+ +---------+ +---------+ +---------+ - / - / -+-----+ +-----+ +-----+ +-----+ +-----+ -! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk -+-----+ +-----+ +-----+ +-----+ +-----+ - ! - ! - ! +---------+ +---------+ +---------+ -Branch 1.2.4 -> +---! 1.2.4.1 !----! 1.2.4.2 !----! 1.2.4.3 ! - +---------+ +---------+ +---------+ - -@end group -@end example - -@c -- However, at least for me the figure is not enough. I suggest more -@c -- text to accompany it. "A picture is worth a thousand words", so you -@c -- have to make sure the reader notices the couple of hundred words -@c -- *you* had in mind more than the others! - -@c -- Why an even number of segments? This section implies that this is -@c -- how the main trunk is distinguished from branch roots, but you never -@c -- explicitly say that this is the purpose of the [by itself rather -@c -- surprising] restriction to an even number of segments. - -The exact details of how the branch number is -constructed is not something you normally need to be -concerned about, but here is how it works: When -@sc{cvs} creates a branch number it picks the first -unused even integer, starting with 2. So when you want -to create a branch from revision 6.4 it will be -numbered 6.4.2. All branch numbers ending in a zero -(such as 6.4.0) are used internally by @sc{cvs} -(@pxref{Magic branch numbers}). The branch 1.1.1 has a -special meaning. @xref{Tracking sources}. - -@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@node Versions revisions releases -@section Versions, revisions and releases -@cindex Revisions, versions and releases -@cindex Versions, revisions and releases -@cindex Releases, revisions and versions - -A file can have several versions, as described above. -Likewise, a software product can have several versions. -A software product is often given a version number such -as @samp{4.1.1}. - -Versions in the first sense are called @dfn{revisions} -in this document, and versions in the second sense are -called @dfn{releases}. To avoid confusion, the word -@dfn{version} is almost never used in this document. - @c --------------------------------------------------------------------- @node A sample session @chapter A sample session @@ -668,9 +671,39 @@ called @dfn{releases}. To avoid confusion, the word @cindex tc, Trivial Compiler (example) @cindex Trivial Compiler (example) -This section describes a typical work-session using -@sc{cvs}. It assumes that a repository is set up -(@pxref{Repository}). +@c I think an example is a pretty good way to start. But +@c somewhere in here, maybe after the sample session, +@c we need something which is kind of +@c a "roadmap" which is more directed at sketching out +@c the functionality of CVS and pointing people to +@c various other parts of the manual. As it stands now +@c people who read in order get dumped right into all +@c manner of hair regarding remote repositories, +@c creating a repository, etc. +@c +@c The following was in the old Basic concepts node. I don't +@c know how good a job it does at introducing modules, +@c or whether they need to be introduced so soon, but +@c something of this sort might go into some +@c introductory material somewhere. +@ignore +@cindex Modules (intro) +The repository contains directories and files, in an +arbitrary tree. The @dfn{modules} feature can be used +to group together a set of directories or files into a +single entity (@pxref{modules}). A typical usage is to +define one module per project. +@end ignore + +As a way of introducing @sc{cvs}, we'll go through a +typical work-session using @sc{cvs}. The first thing +to understand is that @sc{cvs} stores all files in a +centralized @dfn{repository} (@pxref{Repository}); this +section assumes that a repository is set up. +@c I'm not sure that the sentence concerning the +@c repository quite tells the user what they need to +@c know at this point. Might need to expand on "centralized" +@c slightly (maybe not here, maybe further down in the example?) Suppose you are working on a simple compiler. The source consists of a handful of C files and a @file{Makefile}. @@ -707,7 +740,7 @@ the source files. @example $ cd tc -$ ls tc +$ ls CVS Makefile backend.c driver.c frontend.c parser.c @end example @@ -718,7 +751,7 @@ any of the files in it. You start your favorite editor, hack away at @file{backend.c}, and a couple of hours later you have added an optimization pass to the compiler. A note to @sc{rcs} and @sc{sccs} users: There is no need to lock the files that -you want to edit. @xref{Multiple developers} for an explanation. +you want to edit. @xref{Multiple developers}, for an explanation. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Committing your changes @@ -729,7 +762,10 @@ you want to edit. @xref{Multiple developers} for an explanation. @cindex EDITOR, environment variable When you have checked that the compiler is still compilable you decide -to make a new version of @file{backend.c}. +to make a new version of @file{backend.c}. This will +store your new @file{backend.c} in the repository and +make it available to anyone else who is using that same +repository. @example $ cvs commit backend.c @@ -744,8 +780,34 @@ The environment variable @code{$CVSEDITOR} determines which editor is started. If @code{$CVSEDITOR} is not set, then if the environment variable @code{$EDITOR} is set, it will be used. If both @code{$CVSEDITOR} and -@code{$EDITOR} are not set then the editor defaults to -@code{vi}. If you want to avoid the overhead of +@code{$EDITOR} are not set then there is a default +which will vary with your operating system, for example +@code{vi} for unix or @code{notepad} for Windows +NT/95. + +@c This probably should go into some new node +@c containing detailed info on the editor, rather than +@c the intro. In fact, perhaps some of the stuff with +@c CVSEDITOR and -m and so on should too. +When @sc{cvs} starts the editor, it includes a list of +files which are modified. For the @sc{cvs} client, +this list is based on comparing the modification time +of the file against the modification time that the file +had when it was last gotten or updated. Therefore, if +a file's modification time has changed but its contents +have not, it will show up as modified. The simplest +way to handle this is simply not to worry about it---if +you proceed with the commit @sc{cvs} will detect that +the contents are not modified and treat it as an +unmodified file. The next @code{update} will clue +@sc{cvs} in to the fact that the file is unmodified, +and it will reset its stored timestamp so that the file +will not show up in future editor sessions. +@c FIXCVS: Might be nice if "commit" and other commands +@c would reset that timestamp too, but currently commit +@c doesn't. + +If you want to avoid starting an editor you can specify the log message on the command line using the @samp{-m} flag instead, like this: @@ -829,6 +891,7 @@ This command runs @code{diff} to compare the version of @file{driver.c} that you checked out with your working copy. When you see the output you remember that you added a command line option that enabled the optimization pass. You check it in, and release the module. +@c FIXME: we haven't yet defined the term "check in". @example $ cvs commit -m "Added an optimization pass" driver.c @@ -846,52 +909,84 @@ Are you sure you want to release (and delete) module `tc': y @c --------------------------------------------------------------------- @node Repository @chapter The Repository +@cindex Repository (intro) @cindex Repository, example @cindex Layout of repository @cindex Typical repository -@cindex CVSROOT, environment variable -@cindex .profile -@cindex .cshrc -@cindex .tcshrc -@cindex .bashrc -@cindex /usr/local/cvsroot +@cindex /usr/local/cvsroot, as example repository @cindex cvsroot -Figure 3 below shows a typical setup of a repository. -Only directories are shown below. +The @sc{cvs} @dfn{repository} stores a complete copy of +all the files and directories which are under version +control. -@example -@t{/usr} - | - +--@t{local} - | | - | +--@t{cvsroot} - | | | - | | +--@t{CVSROOT} - | (administrative files) - | - +--@t{gnu} - | | - | +--@t{diff} - | | (source code to @sc{gnu} diff) - | | - | +--@t{rcs} - | | (source code to @sc{rcs}) - | | - | +--@t{cvs} - | (source code to @sc{cvs}) - | - +--@t{yoyodyne} - | - +--@t{tc} - | | - | +--@t{man} - | | - | +--@t{testing} - | - +--(other Yoyodyne software) -@end example +Normally, you never access any of the files in the +repository directly. Instead, you use @sc{cvs} +commands to get your own copy of the files into a +@dfn{working directory}, and then +work on that copy. When you've finished a set of +changes, you check (or @dfn{commit}) them back into the +repository. The repository then contains the changes +which you have made, as well as recording exactly what +you changed, when you changed it, and other such +information. Note that the repository is not a +subdirectory of the working directory, or vice versa; +they should be in separate locations. +@c Need some example, e.g. repository +@c /usr/local/cvsroot; working directory +@c /home/joe/sources. But this node is too long +@c as it is; need a little reorganization... + +@cindex :local: +@sc{Cvs} can access a repository by a variety of +means. It might be on the local computer, or it might +be on a computer across the room or across the world. +To distinguish various ways to access a repository, the +repository name can start with an @dfn{access method}. +For example, the access method @code{:local:} means to +access a repository directory, so the repository +@code{:local:/usr/local/cvsroot} means that the +repository is in @file{/usr/local/cvsroot} on the +computer running @sc{cvs}. For information on other +access methods, see @ref{Remote repositories}. + +@c Can se say this more concisely? Like by passing +@c more of the buck to the Remote repositories node? +If the access method is omitted, then if the repository +does not contain @samp{:}, then @code{:local:} is +assumed. If it does contain @samp{:} than either +@code{:ext:} or @code{:server:} is assumed. For +example, if you have a local repository in +@file{/usr/local/cvsroot}, you can use +@code{/usr/local/cvsroot} instead of +@code{:local:/usr/local/cvsroot}. But if (under +Windows NT, for example) your local repository is +@file{c:\src\cvsroot}, then you must specify the access +method, as in @code{:local:c:\src\cvsroot}. + +@c This might appear to go in Repository storage, but +@c actually it is describing something which is quite +@c user-visible, when you do a "cvs co CVSROOT". This +@c isn't necessary the perfect place for that, though. +The repository is split in two parts. @file{$CVSROOT/CVSROOT} contains +administrative files for @sc{cvs}. The other directories contain the actual +user-defined modules. + +@menu +* Specifying a repository:: Telling CVS where your repository is +* Repository storage:: The structure of the repository +* Working directory storage:: The structure of working directories +* Intro administrative files:: Defining modules +* Multiple repositories:: Multiple repositories +* Creating a repository:: Creating a repository +* Backing up:: Backing up a repository +* Remote repositories:: Accessing repositories on remote machines +* Read-only access:: Granting read-only access to the repository +* Server temporary directory:: The server creates temporary directories +@end menu +@node Specifying a repository +@section Telling CVS where your repository is There are a couple of different ways to tell @sc{cvs} where to find the repository. You can name the @@ -902,6 +997,11 @@ repository on the command line explicitly, with the cvs -d /usr/local/cvsroot checkout yoyodyne/tc @end example +@cindex .profile, setting CVSROOT in +@cindex .cshrc, setting CVSROOT in +@cindex .tcshrc, setting CVSROOT in +@cindex .bashrc, setting CVSROOT in +@cindex CVSROOT, environment variable Or you can set the @code{$CVSROOT} environment variable to an absolute path to the root of the repository, @file{/usr/local/cvsroot} in this example. @@ -922,6 +1022,8 @@ CVSROOT=/usr/local/cvsroot export CVSROOT @end example +@cindex Root file, in CVS directory +@cindex CVS/Root file A repository specified with @code{-d} will override the @code{$CVSROOT} environment variable. Once you've checked a working copy out from the @@ -929,35 +1031,93 @@ repository, it will remember where its repository is (the information is recorded in the @file{CVS/Root} file in the working copy). -The @code{-d} option and the @file{CVS/Root} file -both override the @code{$CVSROOT} environment variable; -however, @sc{CVS} will complain if the @file{-d} -argument and the @file{CVS/Root} file disagree. - -There is nothing magical about the name -@file{/usr/local/cvsroot}. You can choose to place the -repository anywhere you like. -@xref{Remote repositories} to learn how the repository can be on a -different machine than your working copy of the sources. +The @code{-d} option and the @file{CVS/Root} file both +override the @code{$CVSROOT} environment variable. If +@code{-d} option differs from @file{CVS/Root}, the +former is used (and specifying @code{-d} will cause +@file{CVS/Root} to be updated). Of course, for proper +operation they should be two ways of referring to the +same repository. -The repository is split in two parts. @file{$CVSROOT/CVSROOT} contains -administrative files for @sc{cvs}. The other directories contain the actual -user-defined modules. +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Repository storage +@section How data is stored in the repository +@cindex Repository, how data is stored + +For most purposes it isn't important @emph{how} +@sc{cvs} stores information in the repository. In +fact, the format has changed in the past, and is likely +to change in the future. Since in almost all cases one +accesses the repository via @sc{cvs} commands; such +changes need not be disruptive. + +However, in some cases it may be necessary to +understand how @sc{cvs} stores data in the repository, +for example you might need to track down @sc{cvs} locks +(@pxref{Concurrency}) or you might need to deal with +the file permissions appropriate for the repository. @menu -* User modules:: The structure of the repository -* Intro administrative files:: Defining modules -* Multiple repositories:: Multiple repositories -* Creating a repository:: Creating a repository -* Remote repositories:: Accessing repositories on remote machines +* Repository files:: What files are stored in the repository +* File permissions:: File permissions +* Attic:: Some files are stored in the Attic @end menu -@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@node User modules -@section User modules -@cindex User modules -@cindex Repository, user parts +@node Repository files +@subsection Where files are stored within the repository + +The overall structure of the repository is a directory +tree corresponding to the directories in the working +directory. For example, supposing the repository is in + +@example +/usr/local/cvsroot +@end example + +@noindent +here is a possible directory tree (showing only the +directories): + +@example +@t{/usr} + | + +--@t{local} + | | + | +--@t{cvsroot} + | | | + | | +--@t{CVSROOT} + | (administrative files) + | + +--@t{gnu} + | | + | +--@t{diff} + | | (source code to @sc{gnu} diff) + | | + | +--@t{rcs} + | | (source code to @sc{rcs}) + | | + | +--@t{cvs} + | (source code to @sc{cvs}) + | + +--@t{yoyodyne} + | + +--@t{tc} + | | + | +--@t{man} + | | + | +--@t{testing} + | + +--(other Yoyodyne software) +@end example +With the directories are @dfn{history files} for each file +under version control. The name of the history file is +the name of the corresponding file with @samp{,v} +appended to the end. Here is what the repository for +the @file{yoyodyne/tc} directory might look like: +@c FIXME: Should also mention CVS (CVSREP) +@c FIXME? Should we introduce Attic with an xref to +@c Attic? Not sure whether that is a good idea or not. @example @code{$CVSROOT} | @@ -982,32 +1142,43 @@ user-defined modules. @cindex History files @cindex RCS history files -@cindex RCS, CVS uses RCS -The figure above shows the contents of the @samp{tc} -module inside the repository. As you can see all file -names end in @samp{,v}. The files are @dfn{history -files}. They contain, among other things, enough +@c The first sentence, about what history files +@c contain, is kind of redundant with our intro to what the +@c repository does in node Repository.... +The history files contain, among other things, enough information to recreate any revision of the file, a log of all commit messages and the user-name of the person -who committed the revision. @sc{cvs} uses the -facilities of @sc{rcs}, a simpler version control -system, to maintain these files. For a full +who committed the revision. The history files are +known as @dfn{RCS files}, because the first program to +store files in that format was a version control system +known as @sc{rcs}. For a full description of the file format, see the @code{man} page -@cite{rcsfile(5)}. -@c -- Use this format for all references to man pages, -@c -- or use something better! - -@menu -* File permissions:: File permissions -@end menu +@cite{rcsfile(5)}, distributed with @sc{rcs}. This +file format has become very common---many systems other +than @sc{cvs} or @sc{rcs} can at least import history +files in this format. +@c FIXME: Think about including documentation for this +@c rather than citing it? In the long run, getting +@c this to be a standard (not sure if we can cope with +@c a standards process as formal as IEEE/ANSI/ISO/etc, +@c though...) is the way to go, so maybe citing is +@c better. + +The @sc{rcs} files used in @sc{cvs} differ in a few +ways from the standard format. The biggest difference +is magic branches; for more information see @ref{Magic +branch numbers}. Also in @sc{cvs} the valid tag names +are a subset of what @sc{rcs} accepts; for @sc{cvs}'s +rules see @ref{Tags}. @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @node File permissions @subsection File permissions -@c -- Move this to @node Setting up +@c -- Move this to @node Creating a repository or similar @cindex Security @cindex File permissions @cindex Group +@cindex read-only files, in repository All @samp{,v} files are created read-only, and you should not change the permission of those files. The directories inside the repository should be writable by @@ -1017,15 +1188,62 @@ create a UNIX group (see group(5)) consisting of the persons that are to edit the files in a project, and set up the repository so that it is that group that owns the directory. +@c See also comment in commitinfo node regarding cases +@c which are really awkward with unix groups. This means that you can only control access to files on a per-directory basis. +Note that users must also have write access to check +out files, because @sc{cvs} needs to create lock files +(@pxref{Concurrency}). + +@c CVS seems to use CVSUMASK in picking permissions for +@c val-tags, but maybe we should say more about this. +@c Like val-tags gets created by someone who doesn't +@c have CVSUMASK set right? +Also note that users must have write access to the +@file{CVSROOT/val-tags} file. @sc{Cvs} uses it to keep +track of what tags are valid tag names (it is sometimes +updated when tags are used, as well as when they are +created, though). + +@cindex CVSUMASK +@cindex umask, for repository files @sc{cvs} tries to set up reasonable file permissions for new directories that are added inside the tree, but you must fix the permissions manually when a new directory should have different permissions than its -parent directory. +parent directory. If you set the @code{CVSUMASK} +environment variable that will control the file +permissions which @sc{cvs} uses in creating directories +and/or files in the repository. @code{CVSUMASK} does +not affect the file permissions in the working +directory; such files have the permissions which are +typical for newly created files, except that sometimes +@sc{cvs} creates them read-only (see the sections on +watches, @ref{Setting a watch}; -r, @ref{Global +options}; or CVSREAD, @ref{Environment variables}). + +Note that using the client/server @sc{cvs} +(@pxref{Remote repositories}), there is no good way to +set @code{CVSUMASK}; the setting on the client machine +has no effect. If you are connecting with @code{rsh}, you +can set @code{CVSUMASK} in @file{.bashrc} or @file{.cshrc}, as +described in the documentation for your operating +system. This behavior might change in future versions +of @sc{cvs}; do not rely on the setting of +@code{CVSUMASK} on the client having no effect. +@c FIXME: need to explain what a umask is or cite +@c someplace which does. +@c FIXME: Need one place which discusses this +@c read-only files thing. Why would one use -r or +@c CVSREAD? Why would one use watches? How do they interact? +@c FIXME: We need to state +@c whether using CVSUMASK removes the need for manually +@c fixing permissions (in fact, if we are going to mention +@c manually fixing permission, we better document a lot +@c better just what we mean by "fix"). @cindex setuid @cindex setgid @@ -1033,6 +1251,285 @@ Since @sc{cvs} was not written to be run setuid, it is unsafe to try to run it setuid. You cannot use the setuid features of @sc{rcs} together with @sc{cvs}. +@node Attic +@subsection The attic +@cindex attic + +You will notice that sometimes @sc{cvs} stores an +@sc{rcs} file in the @code{Attic}. For example, if the +@sc{cvsroot} is @file{/usr/local/cvsroot} and we are +talking about the file @file{backend.c} in the +directory @file{yoyodyne/tc}, then the file normally +would be in + +@example +/usr/local/cvsroot/yoyodyne/tc/backend.c,v +@end example + +but if it goes in the attic, it would be in + +@example +/usr/local/cvsroot/yoyodyne/tc/Attic/backend.c,v +@end example + +@cindex dead state +instead. It should not matter from a user point of +view whether a file is in the attic; @sc{cvs} keeps +track of this and looks in the attic when it needs to. +But in case you want to know, the rule is that the RCS +file is stored in the attic if and only if the head +revision on the trunk has state @code{dead}. A +@code{dead} state means that file has been removed, or +never added, for that revision. For example, if you +add a file on a branch, it will have a trunk revision +in @code{dead} state, and a branch revision in a +non-@code{dead} state. +@c Probably should have some more concrete examples +@c here, or somewhere (not sure exactly how we should +@c arrange the discussion of the dead state, versus +@c discussion of the attic). + +@node Working directory storage +@section How data is stored in the working directory + +While we are discussing @sc{cvs} internals which may +become visible from time to time, we might as well talk +about what @sc{cvs} puts in the @file{CVS} directories +in the working directories. As with the repository, +@sc{cvs} handles this information and one can usually +access it via @sc{cvs} commands. But in some cases it +may be useful to look at it, and other programs, such +as the @code{jCVS} graphical user interface or the +@code{VC} package for emacs, may need to look at it. +Such programs should follow the recommendations in this +section if they hope to be able to work with other +programs which use those files, including future +versions of the programs just mentioned and the +command-line @sc{cvs} client. + +The @file{CVS} directory contains several files. +Programs which are reading this directory should +silently ignore files which are in the directory but +which are not documented here, to allow for future +expansion. + +@table @file +@item Root +This file contains the current @sc{cvs} root, as +described in @ref{Specifying a repository}. + +@cindex Repository file, in CVS directory +@cindex CVS/Repository file +@item Repository +This file contains the directory within the repository +which the current directory corresponds with. For +historical reasons it is an absolute pathname, although +it would make more sense for it to be relative to the +root. For example, after the command + +@example +cvs -d :local:/usr/local/cvsroot checkout yoyodyne/tc +@end example + +@file{Root} will contain + +@example +:local:/usr/local/cvsroot +@end example + +and @file{Repository} will contain + +@example +/usr/local/cvsroot/yoydyne/tc +@end example + +@cindex Entries file, in CVS directory +@cindex CVS/Entries file +@item Entries +This file lists the files and directories in the +working directory. It is a text file according to the +conventions appropriate for the operating system in +question. +@c That seems like a lose, it makes it impossible (it +@c would seem) to share a working directory via a +@c networked file system between systems with diverse +@c text file conventions. But it seems to be how CVS +@c currently works. +The first character of each line indicates what sort of +line it is. If the character is unrecognized, programs +reading the file should silently skip that line, to +allow for future expansion. + +If the first character is @samp{/}, then the format is: + +@example +/@var{name}/@var{revision}/@var{timestamp}[+@var{conflict}]/@var{options}/@var{tagdate} +@end example + +where @samp{[} and @samp{]} are not part of the entry, +but instead indicate that the @samp{+} and conflict +marker are optional. @var{name} is the name of the +file within the directory. @var{revision} is the +revision that the file in the working derives from, or +@samp{0} for an added file, or @samp{-} followed by a +revision for a removed file. @var{timestamp} is the +timestamp of the file at the time that @sc{cvs} created +it; if the timestamp differs with the actual +modification time of the file it means the file has +been modified. It is in Universal Time (UT), stored in +the format used by the ISO C asctime() function (for +example, @samp{Sun Apr 7 01:29:26 1996}). One may +write a string which is not in that format, for +example, @samp{Result of merge}, to indicate that the +file should always be considered to be modified. This +is not a special case; to see whether a file is +modified a program should take the timestamp of the file +and simply do a string compare with @var{timestamp}. +@var{conflict} indicates that there was +a conflict; if it is the same as the actual +modification time of the file it means that the user +has obviously not resolved the conflict. @var{options} +contains sticky options (for example @samp{-kb} for a +binary file). @var{tagdate} contains @samp{T} followed +by a tag name, or @samp{D} for a date, followed by a +sticky tag or date. Note that if @var{timestamp} +contains a pair of timestamps separated by a space, +rather than a single timestamp, you are dealing with a +version of @sc{cvs} earlier than @sc{cvs} 1.5 (not +documented here). + +If the first character of a line in @file{Entries} is +@samp{D}, then it indicates a subdirectory. @samp{D} +on a line all by itself indicates that the program +which wrote the @file{Entries} file does record +subdirectories (therefore, if there is such a line and +no other lines beginning with @samp{D}, one knows there +are no subdirectories). Otherwise, the line looks +like: + +@example +D/@var{name}/@var{filler1}/@var{filler2}/@var{filler3}/@var{filler4} +@end example + +where @var{name} is the name of the subdirectory, and +all the @var{filler} fields should be silently ignored, +for future expansion. Programs which modify +@code{Entries} files should preserve these fields. + +@cindex Entries.Log file, in CVS directory +@cindex CVS/Entries.Log file +@item Entries.Log +This file does not record any information beyond that +in @file{Entries}, but it does provide a way to update +the information without having to rewrite the entire +@file{Entries} file, including the ability to preserve +the information even if the program writing +@file{Entries} and @file{Entries.Log} abruptly aborts. +Programs which are reading the @file{Entries} file +should also check for @file{Entries.Log}. If the latter +exists, they should read @file{Entries} and then apply +the changes mentioned in @file{Entries.Log}. After +applying the changes, the recommended practice is to +rewrite @file{Entries} and then delete @file{Entries.Log}. +The format of a line in @file{Entries.Log} is a single +character command followed by a space followed by a +line in the format specified for a line in +@file{Entries}. The single character command is +@samp{A} to indicate that the entry is being added, +@samp{R} to indicate that the entry is being removed, +or any other character to indicate that the entire line +in @file{Entries.Log} should be silently ignored (for +future expansion). If the second character of the line +in @file{Entries.Log} is not a space, then it was +written by an older version of @sc{cvs} (not documented +here). + +@cindex Entries.Backup file, in CVS directory +@cindex CVS/Entries.Backup file +@item Entries.Backup +This is a temporary file. Recommended usage is to +write a new entries file to @file{Entries.Backup}, and +then to rename it (atomically, where possible) to @file{Entries}. + +@cindex Entries.Static file, in CVS directory +@cindex CVS/Entries.Static file +@item Entries.Static +The only relevant thing about this file is whether it +exists or not. If it exists, then it means that only +part of a directory was gotten and @sc{cvs} will +not create additional files in that directory. To +clear it, use the @code{update} command with the +@samp{-d} option, which will get the additional files +and remove @file{Entries.Static}. + +@cindex Tag file, in CVS directory +@cindex CVS/Tag file +@cindex Sticky tags/dates, per-directory +@cindex Per-directory sticky tags/dates +@item Tag +This file contains per-directory sticky tags or dates. +The first character is @samp{T} for a branch tag, +@samp{N} for a non-branch tag, or @samp{D} for a date, +or another character to mean the file should be +silently ignored, for future expansion. This character +is followed by the tag or date. Note that +per-directory sticky tags or dates are used for things +like applying to files which are newly added; they +might not be the same as the sticky tags or dates on +individual files. For general information on sticky +tags and dates, see @ref{Sticky tags}. +@c FIXME: This needs to be much better documented, +@c preferably not in the context of "working directory +@c storage". +@c FIXME: The Sticky tags node needs to discuss, or xref to +@c someplace which discusses, per-directory sticky +@c tags and the distinction with per-file sticky tags. + +@cindex Checkin.prog file, in CVS directory +@cindex CVS/Checkin.prog file +@cindex Update.prog file, in CVS directory +@cindex CVS/Update.prog file +@item Checkin.prog +@itemx Update.prog +These files store the programs specified by the +@samp{-i} and @samp{-u} options in the modules file, +respectively. + +@cindex Notify file, in CVS directory +@cindex CVS/Notify file +@item Notify +This file stores notifications (for example, for +@code{edit} or @code{unedit}) which have not yet been +sent to the server. Its format is not yet documented +here. + +@cindex Notify.tmp file, in CVS directory +@cindex CVS/Notify.tmp file +@item Notify.tmp +This file is to @file{Notify} as @file{Entries.Backup} +is to @file{Entries}. That is, to write @file{Notify}, +first write the new contents to @file{Notify.tmp} and +then (atomically where possible), rename it to +@file{Notify}. + +@cindex Base directory, in CVS directory +@cindex CVS/Base directory +@item Base +If watches are in use, then an @code{edit} command +stores the original copy of the file in the @file{Base} +directory. This allows the @code{unedit} command to +operate even if it is unable to communicate with the +server. + +@cindex Template file, in CVS directory +@cindex CVS/Template file +@item Template +This file contains the template specified by the +@file{rcsinfo} file (@pxref{rcsinfo}). It is only used +by the client; the non-client/server @sc{cvs} consults +@file{rcsinfo} directly. +@end table + @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Intro administrative files @section The administrative files @@ -1041,6 +1538,15 @@ setuid features of @sc{rcs} together with @sc{cvs}. @cindex CVSROOT, module name @cindex Defining modules (intro) +@c FIXME: this node should be reorganized into "general +@c information about admin files" and put the "editing +@c admin files" stuff up front rather than jumping into +@c the details of modules right away. Then the +@c Administrative files node can go away, the information +@c on each admin file distributed to a place appropriate +@c to its function, and this node can contain a table +@c listing each file and a @ref to its detailed description. + The directory @file{$CVSROOT/CVSROOT} contains some @dfn{administrative files}. @xref{Administrative files}, for a complete description. You can use @sc{cvs} without any of these files, but @@ -1062,10 +1568,11 @@ diff gnu/diff tc yoyodyne/tc @end example -The @file{modules} file is line oriented. In its simplest form each -line contains the name of the module, whitespace, and the directory -where the module resides. The directory is a path relative to -@code{$CVSROOT}. The last for lines in the example +The @file{modules} file is line oriented. In its +simplest form each line contains the name of the +module, whitespace, and the directory where the module +resides. The directory is a path relative to +@code{$CVSROOT}. The last four lines in the example above are examples of such lines. @c FIXME: might want to introduce the concept of options in modules file @@ -1076,6 +1583,7 @@ uses features that are not explained here. @xref{modules}, for a full explanation of all the available features. +@c FIXME: subsection without node is bogus @subsection Editing administrative files @cindex Editing administrative files @cindex Administrative files, editing them @@ -1110,12 +1618,25 @@ without sharing any code. All you have to do to have several repositories is to specify the appropriate repository, using the @code{CVSROOT} environment variable, the @samp{-d} option to @sc{cvs}, or (once -you have checked out a working directories) by -simply allowing @sc{cvs} to use the repository that was -used to check out the working directory (@pxref{Repository}). - -Notwithstanding, it can be confusing to have two or -more repositories. +you have checked out a working directory) by simply +allowing @sc{cvs} to use the repository that was used +to check out the working directory +(@pxref{Specifying a repository}). + +The big advantage of having multiple repositories is +that they can reside on different servers. The big +disadvantage is that you cannot have a single @sc{cvs} +command recurse into directories which comes from +different repositories. Generally speaking, if you are +thinking of setting up several repositories on the same +machine, you might want to consider using several +directories within the same repository. +@c FIXCVS: the thing about recursing into diverse roots +@c would be nice to fix. But it gets hairy if they are +@c on diverse servers--it isn't clear this is really +@c all that useful. +@c FIXME: Does the FAQ have more about this? I have a +@c dim recollection, but I'm too lazy to check right now. None of the examples in this manual show multiple repositories. @@ -1123,10 +1644,117 @@ repositories. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Creating a repository @section Creating a repository -@c -- Well, how do you do? -See the instructions in the @file{INSTALL} file in the -@sc{cvs} distribution. +@cindex Repository, setting up +@cindex Creating a repository +@cindex Setting up a repository + +To set up a @sc{cvs} repository, first choose the +machine and disk on which you want to store the +revision history of the source files. CPU and memory +requirements are modest---a server with 32M of memory +or even less can handle a fairly large source tree with +a fair amount of activity. To estimate disk space +requirements, if you are importing RCS files from +another system, the size of those files is the +approximate initial size of your repository, or if you +are starting without any version history, a rule of +thumb is to allow for the server approximately three +times the size of the code to be under CVS for the +repository (you will eventually outgrow this, but not +for a while). On the machines on which the developers +will be working, you'll want disk space for +approximately one working directory for each developer +(either the entire tree or a portion of it, depending +on what each developer uses). Don't worry about CPU +and memory requirements for the clients---any machine +with enough capacity to run the operating system in +question should have little trouble. +@c Stuff about memory duplicates Server requirements +@c to some extent. I'm not sure this is a bad thing, +@c though (one is aimed at people who are looking into +@c this carefully, the other is aimed at people who +@c want a rule of thumb). + +The repository should be accessable +(directly or via a networked file system) from all +machines which want to use @sc{cvs} in server or local +mode; the client machines need not have any access to +it other than via the @sc{cvs} protocol. It is not +possible to use @sc{cvs} to read from a repository +which one only has read access to; @sc{cvs} needs to be +able to create lock files (@pxref{Concurrency}). + +@cindex init (subcommand) +To create a repository, run the @code{cvs init} +command. It will set up an empty repository in the +@sc{cvs} root specified in the usual way +(@pxref{Repository}). For example, + +@example +cvs -d /usr/local/cvsroot init +@end example + +@code{cvs init} is careful to never overwrite any +existing files in the repository, so no harm is done if +you run @code{cvs init} on an already set-up +repository. + +@code{cvs init} will enable history logging; if you +don't want that, remove the history file after running +@code{cvs init}. @xref{history file}. + +@node Backing up +@section Backing up a repository +@cindex Repository, backing up +@cindex Backing up, repository + +There is nothing particularly magical about the files +in the repository; for the most part it is possible to +back them up just like any other files. However, there +are a few issues to consider. + +The first is that to be paranoid, one should either not +use @sc{cvs} during the backup, or have the backup +program lock @sc{cvs} while doing the backup. To not +use @sc{cvs}, you might forbid logins to machines which +can access the repository, turn off your @sc{cvs} +server, or similar mechanisms. The details would +depend on your operating system and how you have +@sc{cvs} set up. To lock @sc{cvs}, you would create +@file{#cvs.rfl} locks in each repository directory. +See @ref{Concurrency}, for more on @sc{cvs} locks. +Having said all this, if you just back up without any +of these precautions, the results are unlikely to be +particularly dire. Restoring from backup, the +repository might be in an inconsistent state, but this +would not be particularly hard to fix manually. + +When you restore a repository from backup, assuming +that changes in the repository were made after the time +of the backup, working directories which were not +affected by the failure may refer to revisions which no +longer exist in the repository. Trying to run @sc{cvs} +in such directories will typically produce an error +message. One way to get those changes back into the +repository is as follows: + +@itemize @bullet +@item +Get a new working directory. + +@item +Copy the files from the working directory from before +the failure over to the new working directory (do not +copy the contents of the @file{CVS} directories, of +course). + +@item +Working in the new working directory, use commands such +as @code{cvs update} and @code{cvs diff} to figure out +what has changed, and then when you are ready, commit +the changes into the repository. +@end itemize @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Remote repositories @@ -1134,25 +1762,103 @@ See the instructions in the @file{INSTALL} file in the @cindex Repositories, remote @cindex Remote repositories @cindex Client/Server Operation +@cindex server, CVS Your working copy of the sources can be on a -different machine than the repository. Generally, -using a remote repository is just like using a local -one, except that the format of the repository name is: +different machine than the repository. Using @sc{cvs} +in this manner is known as @dfn{client/server} +operation. You run @sc{cvs} on a machine which can +mount your working directory, known as the +@dfn{client}, and tell it to communicate to a machine +which can mount the repository, known as the +@dfn{server}. Generally, using a remote +repository is just like using a local one, except that +the format of the repository name is: @example - user@@hostname:/path/to/repository +:@var{method}:@var{user}@@@var{hostname}:/path/to/repository @end example The details of exactly what needs to be set up depend on how you are connecting to the server. +If @var{method} is not specified, and the repository +name contains @samp{:}, then the default is @code{ext} +or @code{server}, depending on your platform; both are +described in @ref{Connecting via rsh}. +@c Should we try to explain which platforms are which? +@c Platforms like unix and VMS, which only allow +@c privileged programs to bind to sockets <1024 lose on +@c :server: +@c Platforms like Mac and VMS, whose rsh program is +@c unusable or nonexistent, lose on :ext: +@c Platforms like OS/2 and NT probably could plausibly +@c default either way (modulo -b troubles). + +@c FIXME: We need to have a better way of explaining +@c what method to use. This presentation totally +@c obscures the fact that :ext: and CVS_RSH is the way to +@c use SSH, for example. Plus it incorrectly implies +@c that you need an @code{rsh} binary on the client to use +@c :server:. @menu +* Server requirements:: Memory and other resources for servers * Connecting via rsh:: Using the @code{rsh} program to connect * Password authenticated:: Direct connections using passwords * Kerberos authenticated:: Direct connections with kerberos @end menu +@node Server requirements +@subsection Server requirements + +The quick answer to what sort of machine is suitable as +a server is that requirements are modest---a server +with 32M of memory or even less can handle a fairly +large source tree with a fair amount of activity. +@c Say something about CPU speed too? I'm even less sure +@c what to say on that subject... + +The real answer, of course, is more complicated. The +@sc{cvs} server consists of two processes for each +client that it is serving. Memory consumption on the +child process should remain fairly small. Memory +consumption on the parent process, particularly if the +network connection to the client is slow, can be +expected to grow to slightly more than the size of the +sources in a single directory, or two megabytes, +whichever is larger. +@c "two megabytes" of course is SERVER_HI_WATER. But +@c we don't mention that here because we are +@c documenting the default configuration of CVS. If it +@c is a "standard" thing to change that value, it +@c should be some kind of run-time configuration. +@c +@c See cvsclient.texi for more on the design decision +@c to not have locks in place while waiting for the +@c client, which is what results in memory consumption +@c as high as this. + +Multiplying the size of each @sc{cvs} server by the +number of servers which you expect to have active at +one time should give an idea of memory requirements for +the server. For the most part, the memory consumed by +the parent process probably can be swap space rather +than physical memory. +@c Has anyone verified that notion about swap space? +@c I say it based pretty much on guessing that the +@c ->text of the struct buffer_data only gets accessed +@c in a first in, first out fashion, but I haven't +@c looked very closely. + +Resource consumption for the client or the +non-client/server @sc{cvs} is even more modest---any +machine with enough capacity to run the operating system +in question should have little trouble. +@c Probably we could be saying more about this. +@c I would guess for non-client/server CVS in an NFS +@c environment the biggest issues is the network and +@c the NFS server. + @node Connecting via rsh @subsection Connecting with rsh @@ -1163,19 +1869,19 @@ operations, so the remote user host needs to have a user. For example, suppose you are the user @file{mozart} on -the local machine @file{anklet.grunge.com}, and the -server machine is @file{chainsaw.brickyard.com}. On +the local machine @file{toe.grunge.com}, and the +server machine is @file{chainsaw.yard.com}. On chainsaw, put the following line into the file @file{.rhosts} in @file{bach}'s home directory: @example -anklet.grunge.com mozart +toe.grunge.com mozart @end example Then test that @code{rsh} is working with @example -rsh -l bach chainsaw.brickyard.com echo $PATH +rsh -l bach chainsaw.yard.com 'echo $PATH' @end example @cindex CVS_SERVER @@ -1189,22 +1895,63 @@ or @file{.profile}. Alternately, you can set the environment variable @code{CVS_SERVER} on the client machine to the filename of the server you want to use, for example @file{/usr/local/bin/cvs-1.6}. +@c FIXME: there should be a way to specify the +@c program in CVSROOT, not CVS_SERVER, so that one can use +@c different ones for different roots. e.g. ":server;cvs=cvs-1.6:" +@c instead of ":server:". There is no need to edit @code{inetd.conf} or start a @sc{cvs} server daemon. +@cindex :server: +@cindex :ext: +There are two access methods that you use in CVSROOT +for rsh. @code{:server:} specifies an internal rsh +client, which is supported only by some CVS ports. +@code{:ext:} specifies an external rsh program. By +default this is @code{rsh} but you may set the +@code{CVS_RSH} environment variable to invoke another +program which can access the remote server (for +example, @code{remsh} on HP-UX 9 because @code{rsh} is +something different). It must be a program which can +transmit data to and from the server without modifying +it; for example the Windows NT @code{rsh} is not +suitable since it by default translates between CRLF +and LF. The OS/2 CVS port has a hack to pass @samp{-b} +to @code{rsh} to get around this, but since this could +potentially cause problems for programs other than the +standard @code{rsh}, it may change in the future. If +you set @code{CVS_RSH} to @code{SSH} or some other rsh +replacement, the instructions in the rest of this +section concerning @file{.rhosts} and so on are likely +to be inapplicable; consult the documentation for your rsh +replacement. +@c FIXME: there should be a way to specify the +@c program in CVSROOT, not CVS_RSH, so that one can use +@c different ones for different roots. e.g. ":ext;rsh=remsh:" +@c instead of ":ext:". +@c See also the comment in src/client.c for rationale +@c concerning "rsh" being the default and never +@c "remsh". + Continuing our example, supposing you want to access the module @file{foo} in the repository @file{/usr/local/cvsroot/}, on machine -@file{chainsaw.brickyard.com}, you are ready to go: +@file{chainsaw.yard.com}, you are ready to go: @example -cvs -d bach@@chainsaw.brickyard.com:/user/local/cvsroot checkout foo +cvs -d :ext:bach@@chainsaw.yard.com:/usr/local/cvsroot checkout foo @end example (The @file{bach@@} can be omitted if the username is the same on both the local and remote hosts.) +@c Should we mention "rsh host echo hi" and "rsh host +@c cat" (the latter followed by typing text and ^D) +@c as troubleshooting techniques? Probably yes +@c (people tend to have trouble setting this up), +@c but this kind of thing can be hard to spell out. + @node Password authenticated @subsection Direct connection with password authentication @@ -1229,6 +1976,9 @@ some adjustments on both the server and client sides. @cindex Pserver (subcommand) @cindex password server, setting up @cindex authenticating server, setting up +@c FIXME: this isn't quite right regarding port +@c numbers; CVS looks up "cvspserver" in +@c /etc/services (on unix, but what about non-unix?). On the server side, the file @file{/etc/inetd.conf} needs to be edited so @code{inetd} knows to run the command @code{cvs pserver} when it receives a @@ -1247,7 +1997,8 @@ cvs -b /usr/local/bin pserver @end example The @samp{-b} option specifies the directory which contains -the @sc{rcs} binaries on the server. +the @sc{rcs} binaries on the server. You could also use the +@samp{-T} option to specify a temporary directory. If your @code{inetd} wants a symbolic service name instead of a raw port number, then put this in @@ -1264,11 +2015,20 @@ cvspserver 2401/tcp @code{inetd}, or do whatever is necessary to force it to reread its initialization files. +@c FIXME: should be documenting how to troubleshoot +@c this. One strange situation I ran into recently +@c was that if inetd.conf specifies a non-existent +@c cvs (e.g. /usr/local/bin/cvs doesn't exist in +@c the above example), the client says +@c cvs-1.8 [login aborted]: unrecognized auth response from harvey: +@c which is a very unhelpful response (can it be +@c improved? does inetd log somewhere?) + @cindex CVS passwd file -@cindex passwd file +@cindex passwd (admin file) Because the client stores and transmits passwords in cleartext (almost---see @ref{Password authentication -security} for details), a separate @sc{cvs} password +security}, for details), a separate @sc{cvs} password file may be used, so people don't compromise their regular passwords when they access the repository. This file is @file{$CVSROOT/CVSROOT/passwd} @@ -1302,16 +2062,59 @@ indicates corresponding valid system usernames). In any case, @sc{cvs} will have no privileges which the (valid) user would not have. +@cindex user aliases + It is possible to ``map'' cvs-specific +usernames onto system usernames (i.e., onto system +login names) in the @file{$CVSROOT/CVSROOT/passwd} file +by appending a colon and the system username after the +password. For example: + +@example +cvs:ULtgRLXo7NRxs:kfogel +generic:1sOp854gDF3DY:spwang +anyone:1sOp854gDF3DY:spwang +@end example + + Thus, someone remotely accessing the repository +on @file{chainsaw.yard.com} with the following +command: + +@example +cvs -d :pserver:cvs@@chainsaw.yard.com:/usr/local/cvsroot checkout foo +@end example + + would end up running the server under the +system identity kfogel, assuming successful +authentication. However, the remote user would not +necessarily need to know kfogel's system password, as +the @file{$CVSROOT/CVSROOT/passwd} file might contain a +different password, used only for @sc{cvs}. And as the +example above indicates, it is permissible to map +multiple cvs usernames onto a single system username. + + This feature is designed to allow people +repository access without full system access (in +particular, see @xref{Read-only access}); however, also +@xref{Password authentication security}. Any sort of +repository access very likely implies a degree of +general system access as well. + Right now, the only way to put a password in the @sc{cvs} @file{passwd} file is to paste it there from somewhere else. Someday, there may be a @code{cvs passwd} command. +@c We might also suggest using the @code{htpasswd} command +@c from freely available web servers as well, but that +@c would open up a can of worms in that the users next +@c questions are likely to be "where do I get it?" and +@c "how do I use it?" @node Password authentication client @subsubsection Using the client with password authentication @cindex Login (subcommand) @cindex password client, using @cindex authenticated client, using +@cindex :pserver: Before connecting to the server, the client must @dfn{log in} with the command @code{cvs login}. Logging in verifies a password with the server, and also records @@ -1325,7 +2128,7 @@ argument or the @code{CVSROOT} environment variable. password: @example -cvs -d bach@@chainsaw.brickyard.com:/usr/local/cvsroot login +cvs -d :pserver:bach@@chainsaw.yard.com:/usr/local/cvsroot login CVS password: @end example @@ -1335,11 +2138,10 @@ complaining that the password was incorrect. Once you have logged in, you can force @sc{cvs} to connect directly to the server and authenticate with -the stored password by prefixing the repository with -@samp{:pserver:}: +the stored password: @example -cvs -d :pserver:bach@@chainsaw.brickyard.com:/usr/local/cvsroot checkout foo +cvs -d :pserver:bach@@chainsaw.yard.com:/usr/local/cvsroot checkout foo @end example The @samp{:pserver:} is necessary because without it, @@ -1360,6 +2162,13 @@ trivially encoded to protect them from "innocent" compromise (i.e., inadvertently being seen by a system administrator who happens to look at that file). +@c FIXME: seems to me this needs somewhat more +@c explanation. +@cindex Logout (subcommand) +The password for the currently choosen remote repository +can be removed from the CVS_PASSFILE by using the +@code{cvs logout} command. + The @code{CVS_PASSFILE} environment variable overrides this default. If you use this variable, make sure you set it @emph{before} @code{cvs login} is run. If you @@ -1367,12 +2176,6 @@ were to set it after running @code{cvs login}, then later @sc{cvs} commands would be unable to look up the password for transmission to the server. -@cindex CVS_PASSWORD, environment variable -The @code{CVS_PASSWORD} environment variable overrides -@emph{all} stored passwords. If it is set, @sc{cvs} -will use it for all password-authenticated -connections. - @node Password authentication security @subsubsection Security considerations with password authentication @@ -1384,6 +2187,11 @@ system administrator accidentally looking at the file), and will not prevent even a naive attacker from gaining the password. +@c FIXME: The bit about "access to the repository +@c implies general access to the system is *not* specific +@c to pserver; it applies to kerberos and SSH and +@c everything else too. Should reorganize the +@c documentation to make this clear. The separate @sc{cvs} password file (@pxref{Password authentication server}) allows people to use a different password for repository access than @@ -1393,6 +2201,13 @@ the server system through a variety of means. Thus, repository access implies fairly broad system access as well. It might be possible to modify @sc{cvs} to prevent that, but no one has done so as of this writing. +@c OpenBSD uses chroot() and copies the repository to +@c provide anonymous read-only access (for details see +@c http://www.openbsd.org/anoncvs.shar). While this +@c closes the most obvious holes, I'm not sure it +@c closes enough holes to recommend it (plus it is +@c *very* easy to accidentally screw up a setup of this +@c type). Furthermore, there may be other ways in which having access to @sc{cvs} allows people to gain more general access to the system; noone has done a careful audit. @@ -1408,25 +2223,31 @@ security, get Kerberos. @subsection Direct connection with kerberos @cindex kerberos +@cindex :kserver: The main disadvantage of using rsh is that all the data needs to pass through additional programs, so it may be slower. So if you have kerberos installed you can connect via a direct @sc{tcp} connection, -authenticating with kerberos (note that the data -transmitted is @emph{not} encrypted). +authenticating with kerberos. To do this, @sc{cvs} needs to be compiled with kerberos support; when configuring @sc{cvs} it tries to detect whether kerberos is present or you can use the @file{--with-krb4} flag to configure. +The data transmitted is @emph{not} encrypted by +default. Encryption support must be compiled into both +the client and server; use the +@file{--enable-encryption} configure option to turn it +on. You must then use the @code{-x} global option to +request encryption. + @cindex CVS_CLIENT_PORT You need to edit @code{inetd.conf} on the server machine to run @code{cvs kserver}. The client uses port 1999 by default; if you want to use another port specify it in the @code{CVS_CLIENT_PORT} environment -variable on the client. Set @code{CVS_CLIENT_PORT} to -@samp{-1} to force an rsh connection. +variable on the client. @cindex kinit When you want to use @sc{cvs}, get a ticket in the @@ -1435,11 +2256,139 @@ which allows you to log into the server machine. Then you are ready to go: @example -cvs -d chainsaw.brickyard.com:/user/local/cvsroot checkout foo +cvs -d :kserver:chainsaw.yard.com:/user/local/cvsroot checkout foo @end example -If @sc{cvs} fails to connect, it will fall back to -trying rsh. +Previous versions of @sc{cvs} would fall back to a +connection via rsh; this version will not do so. + +@c --------------------------------------------------------------------- +@node Read-only access +@section Read-only repository access +@cindex read-only repository access +@cindex readers (admin file) +@cindex writers (admin file) + + It is possible to grant read-only repository +access to people using the password-authenticated +server (@pxref{Password authenticated}). (The +other access methods do not have explicit support for +read-only users because those methods all assume login +access to the repository machine anyway, and therefore +the user can do whatever local file permissions allow +her to do.) + + A user who has read-only access can do only +those @sc{cvs} operations which do not modify the +repository, except for certain ``administrative'' files +(such as lock files and the history file). It may be +desirable to use this feature in conjunction with +user-aliasing (@pxref{Password authentication server}). +However, note that read-only access does not repeal the +existing security considerations in @xref{Password +authentication security}. + + There are two ways to specify read-only access +for a user: by inclusion, and by exclusion. + + "Inclusion" means listing that user +specifically in the @file{$CVSROOT/CVSROOT/readers} +file, which is simply a newline-separated list of +users. Here is a sample @file{readers} file: + +@example +melissa +splotnik +jrandom +@end example + + (Don't forget the newline after the last user.) + + "Exclusion" means explicitly listing everyone +who has @emph{write} access---if the file + +@example +$CVSROOT/CVSROOT/writers +@end example + +@noindent +exists, then only +those users listed in it have write access, and +everyone else has read-only access (of course, even the +read-only users still need to be listed in the +@sc{cvs} @file{passwd} file). The +@file{writers} file has the same format as the +@file{readers} file. + + Note: if your @sc{cvs} @file{passwd} +file maps cvs users onto system users (@pxref{Password +authentication server}), make sure you deny or grant +read-only access using the @emph{cvs} usernames, not +the system usernames. That is, the @file{readers} and +@file{writers} files contain cvs usernames, which may +or may not be the same as system usernames. + + Here is a complete description of the server's +behavior in deciding whether to grant read-only or +read-write access: + + If @file{readers} exists, and this user is +listed in it, then she gets read-only access. Or if +@file{writers} exists, and this user is NOT listed in +it, then she also gets read-only access (this is true +even if @file{readers} exists but she is not listed +there). Otherwise, she gets full read-write access. + + Of course there is a conflict if the user is +listed in both files. This is resolved in the more +conservative way, it being better to protect the +repository too much than too little: such a user gets +read-only access. + +@node Server temporary directory +@section Temporary directories for the server +@cindex temporary directories, and server +@cindex server, temporary directories + +While running, the @sc{cvs} server creates temporary +directories. They are named + +@example +cvs-serv@var{pid} +@end example + +@noindent +where @var{pid} is the process identification number of +the server. They are located in the directory +specified by the @samp{TMPDIR} environment variable +(@pxref{Environment variables}), the @samp{-T} global +option (@pxref{Global options}), or failing that +@file{/tmp}. + +In most cases the server will remove the temporary +directory when it is done, whether it finishes normally +or abnormally. However, there are a few cases in which +the server does not or cannot remove the temporary +directory, for example: + +@itemize @bullet +@item +If the server aborts due to an internal server error, +it may preserve the directory to aid in debugging + +@item +If the server is killed in a way that it has no way of +cleaning up (most notably, @samp{kill -KILL} on unix). + +@item +If the system shuts down without an orderly shutdown, +which tells the server to clean up. +@end itemize + +In cases such as this, you will need to manually remove +the @file{cvs-serv@var{pid}} directories. As long as +there is no server running with process identification +number @var{pid}, it is safe to do so. @c --------------------------------------------------------------------- @node Starting a new project @@ -1448,12 +2397,14 @@ trying rsh. @cindex Creating a project @comment --moduledb-- -Since @sc{cvs} 1.x is bad at renaming files and moving -them between directories, the first thing you do when -you start a new project should be to think through your -file organization. It is not impossible---just -awkward---to rename or move files. -@xref{Moving files}. +Because renaming files and moving them between +directories is somewhat inconvenient, the first thing +you do when you start a new project should be to think +through your file organization. It is not impossible +to rename or move files, but it does increase the +potential for confusion and @sc{cvs} does have some +quirks particularly in the area of renaming +directories. @xref{Moving files}. What to do next depends on the situation at hand. @@ -1476,12 +2427,12 @@ be done in a couple of different ways. where files already exists. * From other version control systems:: Old projects where you want to preserve history from another system. -* From scratch:: Creating a module from scratch. +* From scratch:: Creating a directory tree from scratch. @end menu @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @node From files -@subsection Creating a module from a number of files +@subsection Creating a directory tree from a number of files @cindex Importing files When you begin using @sc{cvs}, you will probably already have several @@ -1489,12 +2440,12 @@ projects that can be put under @sc{cvs} control. In these cases the easiest way is to use the @code{import} command. An example is probably the easiest way to explain how to use it. If the files you want to install in -@sc{cvs} reside in @file{@var{dir}}, and you want them to appear in the -repository as @file{$CVSROOT/yoyodyne/@var{dir}}, you can do this: +@sc{cvs} reside in @file{@var{wdir}}, and you want them to appear in the +repository as @file{$CVSROOT/yoyodyne/@var{rdir}}, you can do this: @example -$ cd @var{dir} -$ cvs import -m "Imported sources" yoyodyne/@var{dir} yoyo start +$ cd @var{wdir} +$ cvs import -m "Imported sources" yoyodyne/@var{rdir} yoyo start @end example Unless you supply a log message with the @samp{-m} @@ -1507,12 +2458,15 @@ more information about them. You can now verify that it worked, and remove your original source directory. +@c FIXME: Need to say more about "verify that it +@c worked". What should the user look for in the output +@c from "diff -r"? @example $ cd .. $ mv @var{dir} @var{dir}.orig $ cvs checkout yoyodyne/@var{dir} # @r{Explanation below} -$ ls -R yoyodyne +$ diff -r @var{dir}.orig yoyodyne/@var{dir} $ rm -r @var{dir}.orig @end example @@ -1532,6 +2486,10 @@ It is a good idea to check that the permissions are reasonable, and that they belong to the proper groups. @xref{File permissions}. +If some of the files you want to import are binary, you +may want to use the wrappers features to specify which +files are binary and which are not. @xref{Wrappers}. + @c The node name is too long, but I am having trouble @c thinking of something more concise. @node From other version control systems @@ -1567,6 +2525,16 @@ working directory. @c branches. It could also do something about the case @c where the RCS file had a (non-magic) "0" branch. +The @sc{rcs} file should not be locked when you move it +into @sc{cvs}; if it is, @sc{cvs} will have trouble +letting you operate on it. +@c What is the easiest way to unlock your files if you +@c have them locked? Especially if you have a lot of them? +@c This is a CVS bug/misfeature; importing RCS files +@c should ignore whether they are locked and leave them in +@c an unlocked state. Yet another reason for a separate +@c "import RCS file" command. + @c How many is "many"? Or do they just import RCS files? @item From another version control system Many version control systems have the ability to export @@ -1584,11 +2552,33 @@ Note: you must run it on a machine which has both else in contrib it is unsupported (your mileage may vary). @end table +@c CMZ and/or PATCHY were systems that were used in the +@c high energy physics community (especially for +@c CERNLIB). CERN has replaced them with CVS, but the +@c CAR format seems to live on as a way to submit +@c changes. There is a program car2cvs which converts +@c but I'm not sure where one gets a copy. +@c Not sure it is worth mentioning here, since it would +@c appear to affect only one particular community. +@c Best page for more information is: +@c http://wwwcn1.cern.ch/asd/cvs/index.html +@c See also: +@c http://ecponion.cern.ch/ecpsa/cernlib.html @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @node From scratch -@subsection Creating a module from scratch - +@subsection Creating a directory tree from scratch + +@c Also/instead should be documenting +@c $ cvs co -l . +@c $ mkdir tc +@c $ cvs add tc +@c $ cd tc +@c $ mkdir man +@c $ cvs add man +@c etc. +@c Using import to create the directories only is +@c probably a somewhat confusing concept. For a new project, the easiest thing to do is probably to create an empty directory structure, like this: @@ -1633,8 +2623,8 @@ In simple cases these steps are sufficient to define a module. Get a working copy of the modules file. @example -$ cvs checkout modules -$ cd modules +$ cvs checkout CVSROOT/modules +$ cd CVSROOT @end example @item @@ -1659,7 +2649,7 @@ Release the modules module. @example $ cd .. -$ cvs release -d modules +$ cvs release -d CVSROOT @end example @end enumerate @@ -1671,36 +2661,128 @@ $ cvs release -d modules @cindex File locking @cindex Locking files @cindex Working copy +@cindex reserved checkouts +@cindex unreserved checkouts +@cindex RCS-style locking When more than one person works on a software project things often get complicated. Often, two people try to -edit the same file simultaneously. Some other version -control systems (including @sc{rcs} and @sc{sccs}) -try to solve that particular problem by introducing -@dfn{file locking}, so that only one person can edit -each file at a time. Unfortunately, file locking can -be very counter-productive. If two persons want -to edit different parts of a file, there may be no -reason to prevent either of them from doing so. - -@sc{cvs} does not use file locking. Instead, it allows many -people to edit their own @dfn{working copy} of a file +edit the same file simultaneously. One solution, known +as @dfn{file locking} or @dfn{reserved checkouts}, is +to allow only one person to edit each file at a time. +This is the only solution with some version control +systems, including @sc{rcs} and @sc{sccs}. Currently +the usual way to get reserved checkouts with @sc{cvs} +is the @code{cvs admin -l} command (@pxref{admin +options}). This is not as nicely integrated into +@sc{cvs} as the watch features, described below, but it +seems that most people with a need for reserved +checkouts find it adequate. +@c Or "find it better than worrying about implementing +@c nicely integrated reserved checkouts" or ...? +It also may be possible to use the watches +features described below, together with suitable +procedures (not enforced by software), to avoid having +two people edit at the same time. + +@c Our unreserved checkout model might not +@c be quite the same as others. For example, I +@c think that some systems will tend to create a branch +@c in the case where CVS prints "up-to-date check failed". +@c It isn't clear to me whether we should try to +@c explore these subtleties; it could easily just +@c confuse people. +The default model with @sc{cvs} is known as +@dfn{unreserved checkouts}. In this model, developers +can edit their own @dfn{working copy} of a file simultaneously. The first person that commits his -changes has no automatic way of knowing that another has started to -edit it. Others will get an error message when they -try to commit the file. They must then use @sc{cvs} -commands to bring their working copy up to date with -the repository revision. This process is almost -automatic, and explained in this chapter. - -There are many ways to organize a team of developers. -@sc{cvs} does not try to enforce a certain -organization. It is a tool that can be used in several -ways. It is often useful to inform the group of -commits you have done. @sc{cvs} has several ways of -automating that process. @xref{Informing others}. -@xref{Revision management}, for more tips on how to use -@sc{cvs}. +changes has no automatic way of knowing that another +has started to edit it. Others will get an error +message when they try to commit the file. They must +then use @sc{cvs} commands to bring their working copy +up to date with the repository revision. This process +is almost automatic. + +@c FIXME? should probably use the word "watch" here, to +@c tie this into the text below and above. +@sc{Cvs} also supports mechanisms which facilitate +various kinds of communcation, without actually +enforcing rules like reserved checkouts do. + +The rest of this chapter describes how these various +models work, and some of the issues involved in +choosing between them. + +@ignore +Here is a draft reserved checkout design or discussion +of the issues. This seems like as good a place as any +for this. + +Might want a cvs lock/cvs unlock--in which the names +differ from edit/unedit because the network must be up +for these to work. unedit gives an error if there is a +reserved checkout in place (so that people don't +accidentally leave locks around); unlock gives an error +if one is not in place (this is more arguable; perhaps +it should act like unedit in that case). + +On the other hand, might want it so that emacs, +scripts, etc., can get ready to edit a file without +having to know which model is in use. In that case we +would have a "cvs watch lock" (or .cvsrc?) (that is, +three settings, "on", "off", and "lock"). Having cvs +watch lock set would cause a get to record in the CVS +directory which model is in use, and cause "cvs edit" +to change behaviors. We'd want a way to query which +setting is in effect (this would be handy even if it is +only "on" or "off" as presently). If lock is in +effect, then commit would require a lock before +allowing a checkin; chmod wouldn't suffice (might be +debatable--see chmod comment below, in watches--but it +is the way people expect RCS to work and I can't think +of any significant downside. On the other hand, maybe +it isn't worth bothering, because people who are used +to RCS wouldn't think to use chmod anyway). + +Implementation: use file attributes or use RCS +locking. The former avoids more dependence on RCS +behaviors we will need to reimplement as we librarify +RCS, and makes it easier to import/export RCS files (in +that context, want to ignore the locker field). But +note that RCS locks are per-branch, which is the +correct behavior (this is also an issue for the "watch +on" features; they should be per-branch too). + +Here are a few more random notes about implementation +details, assuming "cvs watch lock" and + +CVS/Watched file? Or try to fit this into CVS/Entries somehow? +Cases: (1) file is checked out (unreserved or with watch on) by old +version of CVS, now we do something with new one, (2) file is checked +out by new version, now we do something with old one. + +Remote protocol would have a "Watched" analogous to "Mode". Of course +it would apply to all Updated-like requests. How do we keep this +setting up to date? I guess that there wants to be a Watched request, +and the server would send a new one if it isn't up to date? (Ugh--hard +to implement and slows down "cvs -q update"--is there an easier way?) + +"cvs edit"--checks CVS/Watched, and if watch lock, then sends +"edit-lock" request. Which comes back with a Checked-in with +appropriate Watched (off, on, lock, locked, or some such?), or error +message if already locked. + +"cvs commit"--only will commit if off/on/locked. lock is not OK. + +Doc: +note that "cvs edit" must be connected to network if watch lock is in +effect. + +Talk about what to do if someone has locked a file and you want to +edit that file. (breaking locks, or lack thereof). + + +@end ignore @menu * File status:: A file can be in several states @@ -1709,6 +2791,7 @@ automating that process. @xref{Informing others}. * Informing others:: To cooperate you must inform * Concurrency:: Simultaneous repository access * Watches:: Mechanisms to track who is editing files +* Choosing a model:: Reserved or unreserved checkouts? @end menu @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1716,35 +2799,151 @@ automating that process. @xref{Informing others}. @section File status @cindex File status @cindex Status of a file -@cindex Four states of a file - -After you have checked out a file out from @sc{cvs}, it is in -one of these four states: +@c Shouldn't this start with an example or something, +@c introducing the unreserved checkout model? Before we +@c dive into listing states? +Based on what operations you have performed on a +checked out file, and what operations others have +performed to that file in the repository, one can +classify a file in a number of states. The states, as +reported by the @code{status} command, are: + +@c The order of items is chosen to group logically +@c similar outputs together. +@c People who want alphabetical can use the index... @table @asis @cindex Up-to-date @item Up-to-date The file is identical with the latest revision in the -repository. -@c -- The above is not always true if branching is used. - -@item Locally modified -@cindex Locally modified +repository for the branch in use. +@c FIXME: should we clarify "in use"? The answer is +@c sticky tags, and trying to distinguish branch sticky +@c tags from non-branch sticky tags seems rather awkward +@c here. +@c FIXME: What happens with non-branch sticky tags? Is +@c a stuck file "Up-to-date" or "Needs checkout" or what? + +@item Locally Modified +@cindex Locally Modified You have edited the file, and not yet committed your changes. -@item Needing update -@cindex Needing update -Someone else has committed a newer revision to the repository. - -@item Needing merge -@cindex Needing merge -Someone else have committed a newer revision to the repository, and you +@item Locally Added +@cindex Locally Added +You have added the file with @code{add}, and not yet +committed your changes. +@c There are many cases involving the file being +@c added/removed/modified in the working directory, and +@c added/removed/modified in the repository, which we +@c don't try to describe here. I'm not sure that "cvs +@c status" produces a non-confusing output in most of +@c those cases. + +@item Locally Removed +@cindex Locally Removed +You have removed the file with @code{remove}, and not yet +committed your changes. + +@item Needs Checkout +@cindex Needs Checkout +Someone else has committed a newer revision to the +repository. The name is slightly misleading; you will +ordinarily use @code{update} rather than +@code{checkout} to get that newer revision. + +@item Needs Patch +@cindex Needs Patch +@c See also newb-123j0 in sanity.sh (although that case +@c should probably be changed rather than documented). +Like Needs Checkout, but the @sc{cvs} server will send +a patch rather than the entire file. Sending a patch or +sending an entire file accomplishes the same thing. + +@item Needs Merge +@cindex Needs Merge +Someone else has committed a newer revision to the repository, and you have also made modifications to the file. -@c -- What about "added" "removed" and so on? + +@item File had conflicts on merge +@cindex File had conflicts on merge +@c is it worth saying that this message was "Unresolved +@c Conflict" in CVS 1.9 and earlier? I'm inclined to +@c think that is unnecessarily confusing to new users. +This is like Locally Modified, except that a previous +@code{update} command gave a conflict. If you have not +already done so, you need to +resolve the conflict as described in @ref{Conflicts example}. + +@item Unknown +@cindex Unknown +@sc{Cvs} doesn't know anything about this file. For +example, you have created a new file and have not run +@code{add}. +@c +@c "Entry Invalid" and "Classify Error" are also in the +@c status.c. The latter definitely indicates a CVS bug +@c (should it be worded more like "internal error" so +@c people submit bug reports if they see it?). The former +@c I'm not as sure; I haven't tracked down whether/when it +@c appears in "cvs status" output. + @end table -You can use the @code{status} command to find out the status of a given -file. @xref{status}. +To help clarify the file status, @code{status} also +reports the @code{Working revision} which is the +revision that the file in the working directory derives +from, and the @code{Repository revision} which is the +latest revision in the repository for the branch in +use. +@c FIXME: should we clarify "in use"? The answer is +@c sticky tags, and trying to distinguish branch sticky +@c tags from non-branch sticky tags seems rather awkward +@c here. +@c FIXME: What happens with non-branch sticky tags? +@c What is the Repository Revision there? See the +@c comment at vn_rcs in cvs.h, which is kind of +@c confused--we really need to document better what this +@c field contains. +@c Q: Should we document "New file!" and other such +@c outputs or are they self-explanatory? +@c FIXME: what about the date to the right of "Working +@c revision"? It doesn't appear with client/server and +@c seems unnecessary (redundant with "ls -l") so +@c perhaps it should be removed for non-client/server too? +@c FIXME: Need some examples. + +@c Would be nice to have an @example showing output +@c from cvs status, with comments showing the xref +@c where each part of the output is described. This +@c might fit in nicely if it is desirable to split this +@c node in two; one to introduce "cvs status" and one +@c to list each of the states. +The options to @code{status} are listed in +@ref{Invoking CVS}. For information on its @code{Sticky tag} +and @code{Sticky date} output, see @ref{Sticky tags}. +For information on its @code{Sticky options} output, +see the @samp{-k} option in @ref{update options}. + +You can think of the @code{status} and @code{update} +commands as somewhat complementary. You use +@code{update} to bring your files up to date, and you +can use @code{status} to give you some idea of what an +@code{update} would do (of course, the state of the +repository might change before you actually run +@code{update}). In fact, if you want a command to +display file status in a more brief format than is +displayed by the @code{status} command, you can invoke + +@cindex update, to display file status +@example +$ cvs -n -q update +@end example + +The @samp{-n} option means to not actually do the +update, but merely to display statuses; the @samp{-q} +option avoids printing the name of each directory. For +more information on the @code{update} command, and +these options, see @ref{Invoking CVS}. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Updating a file @@ -1889,11 +3088,11 @@ int main(int argc, gencode(); else fprintf(stderr, "No code generated.\n"); -<<<<<<< driver.c +@asis{}<<<<<<< driver.c exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE); -======= +@asis{}======= exit(!!nerr); ->>>>>>> 1.6 +@asis{}>>>>>>> 1.6 @} @end example @@ -1947,6 +3146,23 @@ new revision: 1.7; previous revision: 1.6 done @end example +For your protection, @sc{cvs} will refuse to check in a +file if a conflict occurred and you have not resolved +the conflict. Currently to resolve a conflict, you +must change the timestamp on the file, and must also +insure that the file contains no conflict markers. If +your file legitimately contains conflict markers (that +is, occurrences of @samp{>>>>>>> } at the start of a +line that don't mark a conflict), then @sc{cvs} has +trouble handling this and you need to start hacking on +the @code{CVS/Entries} file or other such workarounds. +@c FIXME: There should be a "cvs resolved" command +@c which clears the conflict indication. For a nice user +@c interface, this should be invoked by an interactive +@c merge tool like emerge rather than by the user +@c directly--such a tool can verify that the user has +@c really dealt with each conflict. + @cindex emerge If you use release 1.04 or later of pcl-cvs (a @sc{gnu} Emacs front-end for @sc{cvs}) you can use an Emacs @@ -1974,6 +3190,8 @@ newsgroup. @section Several developers simultaneously attempting to run CVS @cindex locks, cvs +@c For a discussion of *why* CVS creates locks, see +@c the comment at the start of src/lock.c If several developers try to run @sc{cvs} at the same time, one may get the following message: @@ -1987,15 +3205,16 @@ if it still needs to wait. If a lock seems to stick around for an undue amount of time, find the person holding the lock and ask them about the cvs command they are running. If they aren't running a cvs -command, look for and remove files starting with -@file{#cvs.tfl}, @file{#cvs.rfl}, or @file{#cvs.wfl} -from the repository. +command, look in the repository directory mentioned in +the message and remove files which they own whose names +start with @file{#cvs.tfl}, @file{#cvs.rfl}, or +@file{#cvs.wfl}. Note that these locks are to protect @sc{cvs}'s internal data structures and have no relationship to -the word @dfn{lock} in the sense used by @sc{rcs}--a -way to prevent other developers from working on a -particular file. +the word @dfn{lock} in the sense used by +@sc{rcs}---which refers to reserved checkouts +(@pxref{Multiple developers}). Any number of people can be reading from a given repository at a time; only when someone is writing do @@ -2003,6 +3222,17 @@ the locks prevent other people from reading or writing. @cindex Atomic transactions, lack of @cindex Transactions, atomic, lack of +@c the following talks about what one might call commit/update +@c atomicity. +@c Probably also should say something about +@c commit/commit atomicity, that is, "An update will +@c not get partial versions of more than one commit". +@c CVS currently has this property and I guess we can +@c make it a documented feature. +@c For example one person commits +@c a/one.c and b/four.c and another commits a/two.c and +@c b/three.c. Then an update cannot get the new a/one.c +@c and a/two.c and the old b/four.c and b/three.c. One might hope for the following property @example @@ -2049,6 +3279,19 @@ section allow such coordination, while retaining the ability of two developers to edit the same file at the same time. +@c Some people might ask why CVS does not enforce the +@c rule on chmod, by requiring a cvs edit before a cvs +@c commit. The main reason is that it could always be +@c circumvented--one could edit the file, and +@c then when ready to check it in, do the cvs edit and put +@c in the new contents and do the cvs commit. One +@c implementation note: if we _do_ want to have cvs commit +@c require a cvs edit, we should store the state on +@c whether the cvs edit has occurred in the working +@c directory, rather than having the server try to keep +@c track of what working directories exist. +@c FIXME: should the above discussion be part of the +@c manual proper, somewhere, not just in a comment? For maximum benefit developers should use @code{cvs edit} (not @code{chmod}) to make files read-write to edit them, and @code{cvs release} (not @code{rm}) to @@ -2078,8 +3321,9 @@ To enable the watch features, you first specify that certain files are to be watched. @cindex watch on (subcommand) -@deffn Command {cvs watch on} [@code{-l}] files @dots{} +@deffn Command {cvs watch on} [@code{-lR}] files @dots{} +@cindex read-only files, and watches Specify that developers should run @code{cvs edit} before editing @var{files}. CVS will create working copies of @var{files} read-only, to remind developers @@ -2093,18 +3337,20 @@ added in the future; this allows the user to set notification policies on a per-directory basis. The contents of the directory are processed recursively, unless the @code{-l} option is given. +The @code{-R} option can be used to force recursion if the @code{-l} +option is set in @file{~/.cvsrc} (@pxref{~/.cvsrc}). If @var{files} is omitted, it defaults to the current directory. @cindex watch off (subcommand) @end deffn -@deffn Command {cvs watch off} [@code{-l}] files @dots{} +@deffn Command {cvs watch off} [@code{-lR}] files @dots{} Do not provide notification about work on @var{files}. CVS will create working copies of @var{files} read-write. -The @var{files} and @code{-l} arguments are processed as for @code{cvs +The @var{files} and options are processed as for @code{cvs watch on}. @end deffn @@ -2120,7 +3366,7 @@ watch on}, so that developers use the @code{cvs edit} command. @cindex watch add (subcommand) -@deffn Command {cvs watch add} [@code{-a} action] [@code{-l}] files @dots{} +@deffn Command {cvs watch add} [@code{-a} action] [@code{-lR}] files @dots{} Add the current user to the list of people to receive notification of work done on @var{files}. @@ -2154,14 +3400,14 @@ described below.) The @code{-a} option may appear more than once, or not at all. If omitted, the action defaults to @code{all}. -The @var{files} and @code{-l} option are processed as for the +The @var{files} and options are processed as for the @code{cvs watch} commands. @end deffn @cindex watch remove (subcommand) -@deffn Command {cvs watch remove} [@code{-a} action] [@code{-l}] files @dots{} +@deffn Command {cvs watch remove} [@code{-a} action] [@code{-lR}] files @dots{} Remove a notification request established using @code{cvs watch add}; the arguments are the same. If the @code{-a} option is present, only @@ -2169,11 +3415,32 @@ watches for the specified actions are removed. @end deffn +@cindex notify (admin file) When the conditions exist for notification, @sc{cvs} -calls the @file{notify} administrative file, passing it -the user to receive the notification and the user who -is taking the action which results in notification. -Normally @file{notify} will just send an email message. +calls the @file{notify} administrative file. Edit +@file{notify} as one edits the other administrative +files (@pxref{Intro administrative files}). This +file follows the usual conventions for administrative +files (@pxref{syntax}), where each line is a regular +expression followed by a command to execute. The +command should contain a single ocurrence of @samp{%s} +which will be replaced by the user to notify; the rest +of the information regarding the notification will be +supplied to the command on standard input. The +standard thing to put in the @code{notify} file is the +single line: + +@example +ALL mail %s -s \"CVS notification\" +@end example + +This causes users to be notified by electronic mail. +@c FIXME: should it be this hard to set up this +@c behavior (and the result when one fails to do so, +@c silent failure to notify, so non-obvious)? Should +@c CVS give a warning if no line in notify matches (and +@c document the use of "DEFAULT :" for the case where +@c skipping the notification is indeed desired)? @cindex users (admin file) Note that if you set this up in the straightforward @@ -2188,13 +3455,49 @@ instead of passing the name of the user to be notified to @file{notify}, @sc{cvs} will pass the @var{value} (normally an email address on some other machine). +@sc{Cvs} does not notify you for your own changes. +Currently this check is done based on whether the user +name of the person taking the action which triggers +notification matches the user name of the person +getting notification. In fact, in general, the watches +features only track one edit by each user. It probably +would be more useful if watches tracked each working +directory separately, so this behavior might be worth +changing. +@c "behavior might be worth changing" is an effort to +@c point to future directions while also not promising +@c that "they" (as in "why don't they fix CVS to....") +@c will do this. +@c one implementation issue is identifying whether a +@c working directory is same or different. Comparing +@c pathnames/hostnames is hopeless, but having the server +@c supply a serial number which the client stores in the +@c CVS directory as a magic cookie should work. + @node Editing files @subsection How to edit a file which is being watched +@cindex checkout, as term for getting ready to edit Since a file which is being watched is checked out read-only, you cannot simply edit it. To make it -read-write, and inform others that you are planning -to edit it, use the @code{cvs edit} command. +read-write, and inform others that you are planning to +edit it, use the @code{cvs edit} command. Some systems +call this a @dfn{checkout}, but @sc{cvs} uses that term +for obtaining a copy of the sources (@pxref{Getting the +source}), an operation which those systems call a +@dfn{get} or a @dfn{fetch}. +@c Issue to think about: should we transition CVS +@c towards the "get" terminology? "cvs get" is already a +@c synonym for "cvs checkout" and that section of the +@c manual refers to "Getting the source". If this is +@c done, needs to be done gingerly (for example, we should +@c still accept "checkout" in .cvsrc files indefinitely +@c even if the CVS's messages are changed from "cvs checkout: " +@c to "cvs get: "). +@c There is a concern about whether "get" is not as +@c good for novices because it is a more general term +@c than "checkout" (and thus arguably harder to assign +@c a technical meaning for). @cindex edit (subcommand) @deffn Command {cvs edit} [options] files @dots{} @@ -2209,7 +3512,7 @@ user on @var{files}; CVS will remove the watch when @var{files} are @code{unedit}ed or @code{commit}ted. If the user does not wish to receive notifications, she should specify @code{-a none}. -The @var{files} and @code{-l} option are processed as for the @code{cvs +The @var{files} and options are processed as for the @code{cvs watch} commands. @end deffn @@ -2222,7 +3525,9 @@ your changes, or not to make any changes, you can use the @code{cvs unedit} command. @cindex unedit (subcommand) -@deffn Command {cvs unedit} [@code{-l}] files @dots{} +@cindex abandoning work +@cindex reverting to repository version +@deffn Command {cvs unedit} [@code{-lR}] files @dots{} Abandon work on the working files @var{files}, and revert them to the repository versions on which they are based. CVS makes those @@ -2230,9 +3535,18 @@ repository versions on which they are based. CVS makes those @code{cvs watch on}. CVS notifies users who have requested @code{unedit} notification for any of @var{files}. -The @var{files} and @code{-l} option are processed as for the +The @var{files} and options are processed as for the @code{cvs watch} commands. +If watches are not in use, the @code{unedit} command +probably does not work, and the way to revert to the +repository version is to remove the file and then use +@code{cvs update} to get a new copy. The meaning is +not precisely the same; removing and updating may also +bring in some changes which have been made in the +repository since the last time you updated. +@c It would be a useful enhancement to CVS to make +@c unedit work in the non-watch case as well. @end deffn When using client/server @sc{cvs}, you can use the @@ -2245,26 +3559,26 @@ successful @sc{cvs} command. @subsection Information about who is watching and editing @cindex watchers (subcommand) -@deffn Command {cvs watchers} [@code{-l}] files @dots{} +@deffn Command {cvs watchers} [@code{-lR}] files @dots{} List the users currently watching changes to @var{files}. The report includes the files being watched, and the mail address of each watcher. -The @var{files} and @code{-l} arguments are processed as for the +The @var{files} and options are processed as for the @code{cvs watch} commands. @end deffn @cindex editors (subcommand) -@deffn Command {cvs editors} [@code{-l}] files @dots{} +@deffn Command {cvs editors} [@code{-lR}] files @dots{} List the users currently working on @var{files}. The report includes the mail address of each user, the time when the user began working with the file, and the host and path of the working directory containing the file. -The @var{files} and @code{-l} arguments are processed as for the +The @var{files} and options are processed as for the @code{cvs watch} commands. @end deffn @@ -2277,10 +3591,12 @@ If you use the watch features on a repository, it creates @file{CVS} directories in the repository and stores the information about watches in that directory. If you attempt to use @sc{cvs} 1.6 or earlier with the -repository, you get an error message such as +repository, you get an error message such as the +following (all on one line): @example -cvs update: cannot open CVS/Entries for reading: No such file or directory +cvs update: cannot open CVS/Entries for reading: +No such file or directory @end example and your operation will likely be aborted. To use the @@ -2291,31 +3607,264 @@ you cannot upgrade, use the @code{watch off} and that will restore the repository to a state which @sc{cvs} 1.6 can cope with. +@node Choosing a model +@section Choosing between reserved or unreserved checkouts +@cindex choosing, reserved or unreserved checkouts + +Reserved and unreserved checkouts each have pros and +cons. Let it be said that a lot of this is a matter of +opinion or what works given different groups' working +styles, but here is a brief description of some of the +issues. There are many ways to organize a team of +developers. @sc{cvs} does not try to enforce a certain +organization. It is a tool that can be used in several +ways. + +Reserved checkouts can be very counter-productive. If +two persons want to edit different parts of a file, +there may be no reason to prevent either of them from +doing so. Also, it is common for someone to take out a +lock on a file, because they are planning to edit it, +but then forget to release the lock. + +@c "many groups"? specifics? cites to papers on this? +@c some way to weasel-word it a bit more so we don't +@c need facts :-)? +People, especially people who are familiar with +reserved checkouts, often wonder how often conflicts +occur if unreserved checkouts are used, and how +difficult they are to resolve. The experience with +many groups is that they occur rarely and usually are +relatively straightforward to resolve. + +The rarity of serious conflicts may be surprising, until one realizes +that they occur only when two developers disagree on the proper design +for a given section of code; such a disagreement suggests that the +team has not been communicating properly in the first place. In order +to collaborate under @emph{any} source management regimen, developers +must agree on the general design of the system; given this agreement, +overlapping changes are usually straightforward to merge. + +In some cases unreserved checkouts are clearly +inappropriate. If no merge tool exists for the kind of +file you are managing (for example word processor files +or files edited by Computer Aided Design programs), and +it is not desirable to change to a program which uses a +mergeable data format, then resolving conflicts is +going to be unpleasant enough that you generally will +be better off to simply avoid the conflicts instead, by +using reserved checkouts. + +The watches features described above in @ref{Watches} +can be considered to be an intermediate model between +reserved checkouts and unreserved checkouts. When you +go to edit a file, it is possible to find out who else +is editing it. And rather than having the system +simply forbid both people editing the file, it can tell +you what the situation is and let you figure out +whether it is a problem in that particular case or not. +Therefore, for some groups it can be considered the +best of both the reserved checkout and unreserved +checkout worlds. + @c --------------------------------------------------------------------- -@node Branches -@chapter Branches +@node Revisions and branches +@chapter Revisions and branches @cindex Branches @cindex Main trunk and branches @cindex Revision tree, making branches -So far, all revisions shown in this manual have been on -the @dfn{main trunk} -of the revision tree, i.e., all revision numbers -have been of the form @var{x}.@var{y}. One useful -feature, especially when maintaining several releases -of a software product at once, is the ability to make -branches on the revision tree. @dfn{Tags}, symbolic -names for revisions, will also be -introduced in this chapter. +For many uses of @sc{cvs}, one doesn't need to worry +too much about revision numbers; @sc{cvs} assigns +numbers such as @code{1.1}, @code{1.2}, and so on, and +that is all one needs to know. However, some people +prefer to have more knowledge and control concerning +how @sc{cvs} assigns revision numbers. + +If one wants to keep track of a set of revisions +involving more than one file, such as which revisions +went into a particular release, one uses a @dfn{tag}, +which is a symbolic revision which can be assigned to a +numeric revision in each file. + +Another useful feature, especially when maintaining +several releases of a software product at once, is the +ability to make branches on the revision tree. +@c FIXME: probably want another sentence or two, very +@c briefly motivating branches. @menu +* Revision numbers:: The meaning of a revision number +* Versions revisions releases:: Terminology used in this manual +* Assigning revisions:: Assigning revisions * Tags:: Tags--Symbolic revisions * Branches motivation:: What branches are good for * Creating a branch:: Creating a branch * Sticky tags:: Sticky tags +* Magic branch numbers:: Magic branch numbers @end menu @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Revision numbers +@section Revision numbers +@cindex Revision numbers +@cindex Revision tree +@cindex Linear development +@cindex Number, revision- +@cindex Decimal revision number +@cindex Branch number +@cindex Number, branch + +Each version of a file has a unique @dfn{revision +number}. Revision numbers look like @samp{1.1}, +@samp{1.2}, @samp{1.3.2.2} or even @samp{1.3.2.2.4.5}. +A revision number always has an even number of +period-separated decimal integers. By default revision +1.1 is the first revision of a file. Each successive +revision is given a new number by increasing the +rightmost number by one. The following figure displays +a few revisions, with newer revisions to the right. + +@example + +-----+ +-----+ +-----+ +-----+ +-----+ + ! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! + +-----+ +-----+ +-----+ +-----+ +-----+ +@end example + +@c Probably should move the following down a few +@c sections, until after "branch motivation". +@sc{cvs} is not limited to linear development. The +@dfn{revision tree} can be split into @dfn{branches}, +where each branch is a self-maintained line of +development. Changes made on one branch can easily be +moved back to the main trunk. + +Each branch has a @dfn{branch number}, consisting of an +odd number of period-separated decimal integers. The +branch number is created by appending an integer to the +revision number where the corresponding branch forked +off. Having branch numbers allows more than one branch +to be forked off from a certain revision. + +@need 3500 +All revisions on a branch have revision numbers formed +by appending an ordinal number to the branch number. +The following figure illustrates branching with an +example. + +@example +@group + +-------------+ + Branch 1.2.2.3.2 -> ! 1.2.2.3.2.1 ! + / +-------------+ + / + / + +---------+ +---------+ +---------+ +Branch 1.2.2 -> _! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 ! + / +---------+ +---------+ +---------+ + / + / ++-----+ +-----+ +-----+ +-----+ +-----+ +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk ++-----+ +-----+ +-----+ +-----+ +-----+ + ! + ! + ! +---------+ +---------+ +---------+ +Branch 1.2.4 -> +---! 1.2.4.1 !----! 1.2.4.2 !----! 1.2.4.3 ! + +---------+ +---------+ +---------+ + +@end group +@end example + +@c -- However, at least for me the figure is not enough. I suggest more +@c -- text to accompany it. "A picture is worth a thousand words", so you +@c -- have to make sure the reader notices the couple of hundred words +@c -- *you* had in mind more than the others! + +@c -- Why an even number of segments? This section implies that this is +@c -- how the main trunk is distinguished from branch roots, but you never +@c -- explicitly say that this is the purpose of the [by itself rather +@c -- surprising] restriction to an even number of segments. + +The exact details of how the branch number is +constructed is not something you normally need to be +concerned about, but here is how it works: When +@sc{cvs} creates a branch number it picks the first +unused even integer, starting with 2. So when you want +to create a branch from revision 6.4 it will be +numbered 6.4.2. All branch numbers ending in a zero +(such as 6.4.0) are used internally by @sc{cvs} +(@pxref{Magic branch numbers}). The branch 1.1.1 has a +special meaning. @xref{Tracking sources}. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Versions revisions releases +@section Versions, revisions and releases +@cindex Revisions, versions and releases +@cindex Versions, revisions and releases +@cindex Releases, revisions and versions + +A file can have several versions, as described above. +Likewise, a software product can have several versions. +A software product is often given a version number such +as @samp{4.1.1}. + +Versions in the first sense are called @dfn{revisions} +in this document, and versions in the second sense are +called @dfn{releases}. To avoid confusion, the word +@dfn{version} is almost never used in this document. + +@node Assigning revisions +@section Assigning revisions + +@c We avoid the "major revision" terminology. It seems +@c like jargon. Hopefully "first number" is clear enough. +By default, @sc{cvs} will assign numeric revisions by +leaving the first number the same and incrementing the +second number. For example, @code{1.1}, @code{1.2}, +@code{1.3}, etc. + +When adding a new file, the second number will always +be one and the first number will equal the highest +first number of any file in that directory. For +example, the current directory contains files whose +highest numbered revisions are @code{1.7}, @code{3.1}, +and @code{4.12}, then an added file will be given the +numeric revision @code{4.1}. + +@c This is sort of redundant with something we said a +@c while ago. Somewhere we need a better way of +@c introducing how the first number can be anything +@c except "1", perhaps. Also I don't think this +@c presentation is clear on why we are discussing releases +@c and first numbers of numeric revisions in the same +@c breath. +Normally there is no reason to care +about the revision numbers---it is easier to treat them +as internal numbers that @sc{cvs} maintains, and tags +provide a better way to distinguish between things like +release 1 versus release 2 of your product +(@pxref{Tags}). However, if you want to set the +numeric revisions, the @samp{-r} option to @code{cvs +commit} can do that. The @samp{-r} option implies the +@samp{-f} option, in the sense that it causes the +files to be committed even if they are not modified. + +For example, to bring all your files up to +revision 3.0 (including those that haven't changed), +you might invoke: + +@example +$ cvs commit -r 3.0 +@end example + +Note that the number you specify with @samp{-r} must be +larger than any existing revision number. That is, if +revision 3.0 exists, you cannot @samp{cvs commit +-r 1.3}. If you want to maintain several releases in +parallel, you need to use a branch (@pxref{Revisions and branches}). + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Tags @section Tags--Symbolic revisions @cindex Tags @@ -2351,17 +3900,47 @@ rcsutil.c 5.10 You can use the @code{tag} command to give a symbolic name to a certain revision of a file. You can use the @samp{-v} flag to the @code{status} command to see all tags that a file has, and -which revision numbers they represent. Tag names can +which revision numbers they represent. Tag names must +start with an uppercase or lowercase letter and can contain uppercase and lowercase letters, digits, @samp{-}, and @samp{_}. The two tag names @code{BASE} and @code{HEAD} are reserved for use by @sc{cvs}. It is expected that future names which are special to -@sc{cvs} will contain characters such as @samp{%} or -@samp{=}, rather than being named analogously to +@sc{cvs} will be specially named, for example by +starting with @samp{.}, rather than being named analogously to @code{BASE} and @code{HEAD}, to avoid conflicts with actual tag names. -@c FIXME: is the above list of valid characters in tag -@c names complete? +@c Including a character such as % or = has also been +@c suggested as the naming convention for future +@c special tag names. Starting with . is nice because +@c that is not a legal tag name as far as RCS is concerned. +@c FIXME: CVS actually accepts quite a few characters +@c in tag names, not just the ones documented above +@c (see RCS_check_tag). RCS +@c defines legitimate tag names by listing illegal +@c characters rather than legal ones. CVS is said to lose its +@c mind if you try to use "/" (try making such a tag sticky +@c and using "cvs status" client/server--see remote +@c protocol format for entries line for probable cause). +@c TODO: The testsuite +@c should test for whatever are documented above as +@c officially-OK tag names, and CVS should at least reject +@c characters that won't work, like "/". + +You'll want to choose some convention for naming tags, +based on information such as the name of the program +and the version number of the release. For example, +one might take the name of the program, immediately +followed by the version number with @samp{.} changed to +@samp{-}, so that CVS 1.9 would be tagged with the name +@code{cvs1-9}. If you choose a consistent convention, +then you won't constantly be guessing whether a tag is +@code{cvs-1-9} or @code{cvs1_9} or what. You might +even want to consider enforcing your convention in the +taginfo file (@pxref{user-defined logging}). +@c Might be nice to say more about using taginfo this +@c way, like giving an example, or pointing out any particular +@c issues which arise. @cindex Adding a tag @cindex tag, example @@ -2379,7 +3958,7 @@ $ cvs status -v backend.c File: backend.c Status: Up-to-date Version: 1.4 Tue Dec 1 14:39:01 1992 - RCS Version: 1.4 /usr/local/cvsroot/yoyodyne/tc/backend.c,v + RCS Version: 1.4 /u/cvsroot/yoyodyne/tc/backend.c,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) @@ -2506,6 +4085,13 @@ does not require that you have a working copy of the module. @xref{rtag}. (You can also use the @code{tag} command; @pxref{tag}). +@c Why does this example use -r? That seems like a +@c confusing thing to do in an example where we are +@c introducing branches. One user thought it was +@c a mandatory part of creating a branch for example. +@c And we are not sufficiently +@c "step by step" in terms of explaining +@c what argument one should give to -r. @example $ cvs rtag -b -r release-1-0 release-1-0-patches tc @end example @@ -2529,7 +4115,7 @@ $ cvs status -v driver.c backend.c File: driver.c Status: Up-to-date Version: 1.7 Sat Dec 5 18:25:54 1992 - RCS Version: 1.7 /usr/local/cvsroot/yoyodyne/tc/driver.c,v + RCS Version: 1.7 /u/cvsroot/yoyodyne/tc/driver.c,v Sticky Tag: release-1-0-patches (branch: 1.7.2) Sticky Date: (none) Sticky Options: (none) @@ -2542,7 +4128,7 @@ File: driver.c Status: Up-to-date File: backend.c Status: Up-to-date Version: 1.4 Tue Dec 1 14:39:01 1992 - RCS Version: 1.4 /usr/local/cvsroot/yoyodyne/tc/backend.c,v + RCS Version: 1.4 /u/cvsroot/yoyodyne/tc/backend.c,v Sticky Tag: release-1-0-patches (branch: 1.4.2) Sticky Date: (none) Sticky Options: (none) @@ -2560,7 +4146,7 @@ number is created by adding a digit at the tail of the revision number it is based on. (If @samp{release-1-0} corresponds to revision 1.4, the branch's revision number will be 1.4.2. For obscure reasons @sc{cvs} always gives branches even numbers, starting at 2. -@xref{Revision numbers}). +@xref{Revision numbers}.). @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Sticky tags @@ -2596,7 +4182,7 @@ $ cvs status -v driver.c File: driver.c Status: Up-to-date Version: 1.7.2.1 Sat Dec 5 19:35:03 1992 - RCS Version: 1.7.2.1 /usr/local/cvsroot/yoyodyne/tc/driver.c,v + RCS Version: 1.7.2.1 /u/cvsroot/yoyodyne/tc/driver.c,v Sticky Tag: release-1-0-patches (branch: 1.7.2) Sticky Date: (none) Sticky Options: (none) @@ -2616,14 +4202,18 @@ you delete them with @samp{cvs update -A}. The the head of the trunk, and forgets any sticky tags, dates, or options. -@c Is the fact that CVS works this way a bug or a -@c feature? If a feature, describe how you would use -@c it to do something useful. -Sticky tags are not just for branches. If you check -out a certain revision (such as 1.4) it will also -become sticky. Subsequent @samp{cvs update} will not -retrieve the latest revision until you reset the tag -with @samp{cvs update -A}. Likewise, use of the +@cindex sticky date +Sticky tags are not just for branches. For example, +suppose that you want to avoid updating your working +directory, to isolate yourself from possibly +destabilizing changes other people are making. You +can, of course, just refrain from running @code{cvs +update}. But if you want to avoid updating only a +portion of a larger tree, then sticky tags can help. +If you check out a certain revision (such as 1.4) it +will become sticky. Subsequent @code{cvs update} will +not retrieve the latest revision until you reset the +tag with @code{cvs update -A}. Likewise, use of the @samp{-D} option to @code{update} or @code{checkout} sets a @dfn{sticky date}, which, similarly, causes that date to be used for future retrievals. @@ -2648,7 +4238,7 @@ RCS: /tmp/cvs-sanity/cvsroot/first-dir/Attic/file1,v VERS: 1.1 *************** $ cvs add file1 -cvs add: version 1.2 of `file1' will be resurrected +cvs add: re-adding file file1 (in place of dead revision 1.2) cvs add: use 'cvs commit' to add this file permanently $ cvs commit -m test Checking in file1; @@ -2658,6 +4248,73 @@ done $ @end example +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Magic branch numbers +@section Magic branch numbers + +@c Want xref to here from "log" and "admin"? + +This section describes a @sc{cvs} feature called +@dfn{magic branches}. For most purposes, you need not +worry about magic branches; @sc{cvs} handles them for +you. However, they are visible to you in certain +circumstances, so it may be useful to have some idea of +how it works. + +Externally, branch numbers consist of an odd number of +dot-separated decimal integers. @xref{Revision +numbers}. That is not the whole truth, however. For +efficiency reasons @sc{cvs} sometimes inserts an extra 0 +in the second rightmost position (1.2.3 becomes +1.2.0.3, 8.9.10.11.12 becomes 8.9.10.11.0.12 and so +on). + +@sc{cvs} does a pretty good job at hiding these so +called magic branches, but in a few places the hiding +is incomplete: + +@itemize @bullet +@ignore +@c This is in ignore as I'm taking their word for it, +@c that this was fixed +@c a long time ago. But before deleting this +@c entirely, I'd rather verify it (and add a test +@c case to the testsuite). +@item +The magic branch can appear in the output from +@code{cvs status} in vanilla @sc{cvs} 1.3. This is +fixed in @sc{cvs} 1.3-s2. + +@end ignore +@item +The magic branch number appears in the output from +@code{cvs log}. +@c What output should appear instead? + +@item +You cannot specify a symbolic branch name to @code{cvs +admin}. + +@end itemize + +@c Can CVS do this automatically the first time +@c you check something in to that branch? Should +@c it? +You can use the @code{admin} command to reassign a +symbolic name to a branch the way @sc{rcs} expects it +to be. If @code{R4patches} is assigned to the branch +1.4.2 (magic branch number 1.4.0.2) in file +@file{numbers.c} you can do this: + +@example +$ cvs admin -NR4patches:1.4.2 numbers.c +@end example + +It only works if at least one revision is already +committed on the branch. Be very careful so that you +do not assign the tag to the wrong number. (There is +no way to see how the tag was assigned yesterday). + @c --------------------------------------------------------------------- @node Merging @chapter Merging @@ -2676,6 +4333,7 @@ copy the changes onto another branch. * Merging a branch:: Merging an entire branch * Merging more than once:: Merging from a branch several times * Merging two revisions:: Merging differences between two revisions +* Merging adds and removals:: What if files are added or removed? @end menu @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2745,7 +4403,7 @@ like this: @example +-----+ +-----+ +-----+ +-----+ +-----+ -! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk +-----+ +-----+ +-----+ +-----+ +-----+ ! * ! * @@ -2763,7 +4421,7 @@ Now suppose that development continues on the @example +-----+ +-----+ +-----+ +-----+ +-----+ -! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk +-----+ +-----+ +-----+ +-----+ +-----+ ! * ! * @@ -2833,6 +4491,31 @@ that make up a module. You almost always use symbolic tags rather than revision numbers when operating on multiple files. +@node Merging adds and removals +@section Merging can add or remove files + +If the changes which you are merging involve removing +or adding some files, @code{update -j} will reflect +such additions or removals. + +@c FIXME: This example needs a lot more explanation. +@c We also need other examples for some of the other +@c cases (not all--there are too many--as long as we present a +@c coherent general principle). +For example: +@example +cvs update -A +touch a b c +cvs add a b c ; cvs ci -m "added" a b c +cvs tag -b branchtag +cvs update -r branchtag +touch d ; cvs add d +rm a ; cvs rm a +cvs ci -m "added d, removed a" +cvs update -A +cvs update -jbranchtag +@end example + @c --------------------------------------------------------------------- @node Recursive behavior @chapter Recursive behavior @@ -2878,8 +4561,11 @@ following is true: @itemize @bullet @item -@samp{cvs update testing} is equivalent to @samp{cvs -update testing/testpgm.t testing/test2.t} +@samp{cvs update testing} is equivalent to + +@example +cvs update testing/testpgm.t testing/test2.t +@end example @item @samp{cvs update testing man} updates all files in the @@ -2899,6 +4585,8 @@ for most of the @sc{cvs} subcommands, not only the The recursive behavior of the @sc{cvs} subcommands can be turned off with the @samp{-l} option. +Conversely, the @samp{-R} option can be used to force recursion if +@samp{-l} is specified in @file{~/.cvsrc} (@pxref{~/.cvsrc}). @example $ cvs update -l # @r{Don't update files in subdirectories} @@ -2906,63 +4594,138 @@ $ cvs update -l # @r{Don't update files in subdirectories} @c --------------------------------------------------------------------- @node Adding files -@chapter Adding files to a module +@chapter Adding files to a directory @cindex Adding files -To add a new file to a module, follow these steps. +To add a new file to a directory, follow these steps. @itemize @bullet @item -You must have a working copy of the module. +You must have a working copy of the directory. @xref{Getting the source}. @item -Create the new file inside your working copy of the module. +Create the new file inside your working copy of the directory. @item Use @samp{cvs add @var{filename}} to tell @sc{cvs} that you -want to version control the file. +want to version control the file. If the file contains +binary data, specify @samp{-kb} (@pxref{Binary files}). @item Use @samp{cvs commit @var{filename}} to actually check in the file into the repository. Other developers cannot see the file until you perform this step. - -@item -If the file contains binary data it might be necessary -to change the default keyword substitution. -@xref{Keyword substitution}. @xref{admin examples}. @end itemize You can also use the @code{add} command to add a new -directory inside a module. +directory. +@c FIXCVS and/or FIXME: Adding a directory doesn't +@c require the commit step. This probably can be +@c considered a CVS bug, but it is possible we should +@c warn people since this behavior probably won't be +@c changing right away. Unlike most other commands, the @code{add} command is not recursive. You cannot even type @samp{cvs add foo/bar}! Instead, you have to +@c FIXCVS: This is, of course, not a feature. It is +@c just that noone has gotten around to fixing "cvs add +@c foo/bar". @example $ cd foo $ cvs add bar @end example -@xref{add}, for a more complete description of the @code{add} -command. +@cindex add (subcommand) +@deffn Command {cvs add} [@code{-k} kflag] [@code{-m} message] files @dots{} + +Schedule @var{files} to be added to the repository. +The files or directories specified with @code{add} must +already exist in the current directory. To add a whole +new directory hierarchy to the source repository (for +example, files received from a third-party vendor), use +the @code{import} command instead. @xref{import}. + +The added files are not placed in the source repository +until you use @code{commit} to make the change +permanent. Doing an @code{add} on a file that was +removed with the @code{remove} command will undo the +effect of the @code{remove}, unless a @code{commit} +command intervened. @xref{Removing files}, for an +example. + +The @samp{-k} option specifies the default way that +this file will be checked out; for more information see +@ref{Substitution modes}. + +@c As noted in BUGS, -m is broken client/server (Nov +@c 96). Also see testsuite log2-* tests. +The @samp{-m} option specifies a description for the +file. This description appears in the history log (if +it is enabled, @pxref{history file}). It will also be +saved in the version history inside the repository when +the file is committed. The @code{log} command displays +this description. The description can be changed using +@samp{admin -t}. @xref{admin}. If you omit the +@samp{-m @var{description}} flag, an empty string will +be used. You will not be prompted for a description. +@end deffn + +For example, the following commands add the file +@file{backend.c} to the repository: + +@c This example used to specify +@c -m "Optimizer and code generation passes." +@c to the cvs add command, but that doesn't work +@c client/server (see log2 in sanity.sh). Should fix CVS, +@c but also seems strange to document things which +@c don't work... +@example +$ cvs add backend.c +$ cvs commit -m "Early version. Not yet compilable." backend.c +@end example + +When you add a file it is added only on the branch +which you are working on (@pxref{Revisions and branches}). You can +later merge the additions to another branch if you want +(@pxref{Merging adds and removals}). +@c Should we mention that earlier versions of CVS +@c lacked this feature (1.3) or implemented it in a buggy +@c way (well, 1.8 had many bugs in cvs update -j)? +@c Should we mention the bug/limitation regarding a +@c file being a regular file on one branch and a directory +@c on another? +@c FIXME: This needs an example, or several, here or +@c elsewhere, for it to make much sense. +@c Somewhere we need to discuss the aspects of death +@c support which don't involve branching, I guess. +@c Like the ability to re-create a release from a tag. @c --------------------------------------------------------------------- @node Removing files -@chapter Removing files from a module +@chapter Removing files @cindex Removing files @cindex Deleting files +@c FIXME: this node wants to be split into several +@c smaller nodes. Probably would fit well with merging +@c this chapter with "adding files" and the others, as +@c suggested at the top-level menu (death support could +@c be its own section, for example, as could the +@c various bits about undoing mistakes in adding and +@c removing). Modules change. New files are added, and old files disappear. Still, you want to be able to retrieve an -exact copy of old releases of the module. +exact copy of old releases. -Here is what you can do to remove a file from a module, +Here is what you can do to remove a file, but remain able to retrieve old revisions: @itemize @bullet +@c FIXME: should probably be saying something about +@c having a working directory in the first place. @item Make sure that you have not made any uncommitted modifications to the file. @xref{Viewing differences}, @@ -2973,7 +4736,7 @@ course not be able to retrieve the file as it was immediately before you deleted it. @item -Remove the file from your working copy of the module. +Remove the file from your working copy of the directory. You can for instance use @code{rm}. @item @@ -2985,6 +4748,15 @@ Use @samp{cvs commit @var{filename}} to actually perform the removal of the file from the repository. @end itemize +@c FIXME: Somehow this should be linked in with a more +@c general discussion of death support. I don't know +@c whether we want to use the term "death support" or +@c not (we can perhaps get by without it), but we do +@c need to discuss the "dead" state in "cvs log" and +@c related subjects. The current discussion is +@c scattered around, and not xref'd to each other. +@c FIXME: I think this paragraph wants to be moved +@c later down, at least after the first example. When you commit the removal of the file, @sc{cvs} records the fact that the file no longer exists. It is possible for a file to exist on only some branches and @@ -2993,23 +4765,24 @@ name later. CVS will correctly create or not create the file, based on the @samp{-r} and @samp{-D} options specified to @code{checkout} or @code{update}. +@c FIXME: This style seems to clash with how we +@c document things in general. @cindex Remove (subcommand) -@deffn Command {cvs remove} [@code{-lR}] files @dots{} +@deffn Command {cvs remove} [options] files @dots{} Schedule file(s) to be removed from the repository (files which have not already been removed from the working directory are not processed). This command does not actually remove the file from the repository -until you commit the removal. The @samp{-R} option -(the default) specifies that it will recurse into -subdirectories; @samp{-l} specifies that it will not. +until you commit the removal. For a full list of +options, see @ref{Invoking CVS}. @end deffn Here is an example of removing several files: @example $ cd test -$ rm ?.c +$ rm *.c $ cvs remove cvs remove: Removing . cvs remove: scheduling a.c for removal @@ -3020,9 +4793,40 @@ cvs commit: Examining . cvs commit: Committing . @end example -If you change your mind you can easily resurrect the -file before you commit it, using the @code{add} -command. +As a convenience you can remove the file and @code{cvs +remove} it in one step, by specifying the @samp{-f} +option. For example, the above example could also be +done like this: + +@example +$ cd test +$ cvs remove -f *.c +cvs remove: scheduling a.c for removal +cvs remove: scheduling b.c for removal +cvs remove: use 'cvs commit' to remove these files permanently +$ cvs ci -m "Removed unneeded files" +cvs commit: Examining . +cvs commit: Committing . +@end example + +If you execute @code{remove} for a file, and then +change your mind before you commit, you can undo the +@code{remove} with an @code{add} command. +@ignore +@c is this worth saying or not? Somehow it seems +@c confusing to me. +Of course, +since you have removed your copy of file in the working +directory, @sc{cvs} does not necessarily bring back the +contents of the file from right before you executed +@code{remove}; instead it gets the file from the +repository again. +@end ignore + +@c FIXME: what if you change your mind after you commit +@c it? (answer is also "cvs add" but we don't say that...). +@c We need some index entries for thinks like "undoing +@c removal" too. @example $ ls @@ -3047,12 +4851,51 @@ cvs update: warning: oj.c was lost U oj.c @end example +When you remove a file it is removed only on the branch +which you are working on (@pxref{Revisions and branches}). You can +later merge the removals to another branch if you want +(@pxref{Merging adds and removals}). + +@node Removing directories +@chapter Removing directories +@cindex removing directories +@cindex directories, removing + +In concept removing directories is somewhat similar to +removing files---you want the directory to not exist in +your current working directories, but you also want to +be able to retrieve old releases in which the directory +existed. + +The way that you remove a directory is to remove all +the files in it. Then specify the @samp{-P} option to +@code{cvs update}, @code{cvs checkout}, or @code{cvs +export}, which will cause @sc{cvs} to remove empty +directories from working directories. Probably the +best way to do this is to always specify @samp{-P}; if +you want an empty directory then put a dummy file (for +example @file{.keepme}) in it to prevent @samp{-P} from +removing it. + +@c I'd try to give a rationale for this, but I'm not +@c sure there is a particularly convincing one. What +@c we would _like_ is for CVS to do a better job of version +@c controlling whether directories exist, to eliminate the +@c need for -P and so that a file can be a directory in +@c one revision and a regular file in another. +Note that @samp{-P} is implied by the @samp{-r} or @samp{-D} +options of @code{checkout} and @code{export}. This way +@sc{cvs} will be able to correctly create the directory +or not depending on whether the particular version you +are checking out contains any files in that directory. + @c --------------------------------------------------------------------- @node Tracking sources @chapter Tracking third-party sources @cindex Third-party sources @cindex Tracking sources +@c FIXME: Need discussion of added and removed files. If you modify a program to better fit your site, you probably want to include your modifications when the next release of the program arrives. @sc{cvs} can help you with @@ -3083,6 +4926,8 @@ revision. @menu * First import:: Importing a module for the first time * Update imports:: Updating a module with the import command +* Reverting local changes:: Reverting a module to the latest vendor release +* Binary files in imports:: Binary files require special handling @end menu @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3090,26 +4935,37 @@ revision. @section Importing a module for the first time @cindex Importing modules +@c Should mention naming conventions for vendor tags, +@c release tags, and perhaps directory names. Use the @code{import} command to check in the sources for the first time. When you use the @code{import} command to track third-party sources, the @dfn{vendor tag} and @dfn{release tags} are useful. The @dfn{vendor tag} is a symbolic name for the branch (which is always 1.1.1, unless you use the @samp{-b -@var{branch}} flag---@xref{import options}). The +@var{branch}} flag---@xref{import options}.). The @dfn{release tags} are symbolic names for a particular release, such as @samp{FSF_0_04}. +@c I'm not completely sure this belongs here. But +@c we need to say it _somewhere_ reasonably obvious; it +@c is a common misconception among people first learning CVS +Note that @code{import} does @emph{not} change the +directory in which you invoke it. In particular, it +does not set up that directory as a @sc{cvs} working +directory; if you want to work with the sources import +them first and then check them out into a different +directory (@pxref{Getting the source}). + @cindex Wdiff (import example) -Suppose you use @code{wdiff} (a variant of @code{diff} -that ignores changes that only involve whitespace), and -are going to make private modifications that you want -to be able to use even when new releases are made in -the future. You start by importing the source to your -repository: +Suppose you have the sources to a program called +@code{wdiff} in a directory @file{wdiff-0.04}, +and are going to make private modifications that you +want to be able to use even when new releases are made +in the future. You start by importing the source to +your repository: @example -$ tar xfz wdiff-0.04.tar.gz $ cd wdiff-0.04 $ cvs import -m "Import of FSF v. 0.04" fsf/wdiff FSF_DIST WDIFF_0_04 @end example @@ -3117,6 +4973,7 @@ $ cvs import -m "Import of FSF v. 0.04" fsf/wdiff FSF_DIST WDIFF_0_04 The vendor tag is named @samp{FSF_DIST} in the above example, and the only release tag assigned is @samp{WDIFF_0_04}. +@c FIXME: Need to say where fsf/wdiff comes from. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Update imports @@ -3138,6 +4995,10 @@ revision becomes the head revision. If you have made local changes, @code{import} will warn you that you must merge the changes into the main trunk, and tell you to use @samp{checkout -j} to do so. +@c FIXME: why "wdiff" here and "fsf/wdiff" in the +@c "import"? I think the assumption is that one has +@c "wdiff fsf/wdiff" or some such in modules, but it +@c would be better to not use modules in this example. @example $ cvs checkout -jFSF_DIST:yesterday -jFSF_DIST wdiff @end example @@ -3161,6 +5022,32 @@ $ cvs checkout -jWDIFF_0_04 -jWDIFF_0_05 wdiff @noindent In this case, the two above commands are equivalent. +@node Reverting local changes +@section Reverting to the latest vendor release + +You can also revert local changes completely and return +to the latest vendor release by changing the `head' +revision back to the vendor branch on all files. For +example, if you have a checked-out copy of the sources +in @file{~/work.d/wdiff}, and you want to revert to the +vendor's version for all the files in that directory, +you would type: + +@example +$ cd ~/work.d/wdiff +$ cvs admin -bWDIFF . +@end example + +@noindent +You must specify the @samp{-bWDIFF} without any space +after the @samp{-b}. @xref{admin options}. + +@node Binary files in imports +@section How to handle binary files with cvs import + +Use the @samp{-k} wrapper option to tell import which +files are binary. @xref{Wrappers}. + @c --------------------------------------------------------------------- @node Moving files @chapter Moving and renaming files @@ -3171,7 +5058,7 @@ In this case, the two above commands are equivalent. Moving files to a different directory or renaming them is not difficult, but some of the ways in which this works may be non-obvious. (Moving or renaming a -directory is even harder. @xref{Moving directories}). +directory is even harder. @xref{Moving directories}.). The examples below assume that the file @var{old} is renamed to @var{new}. @@ -3186,11 +5073,27 @@ The examples below assume that the file @var{old} is renamed to @node Outside @section The Normal way to Rename +@c More rename issues. Not sure whether these are +@c worth documenting; I'm putting them here because +@c it seems to be as good a place as any to try to +@c set down the issues. +@c * "cvs annotate" will annotate either the new +@c file or the old file; it cannot annotate _each +@c line_ based on whether it was last changed in the +@c new or old file. Unlike "cvs log", where the +@c consequences of having to select either the new +@c or old name seem fairly benign, this may be a +@c real advantage to having CVS know about renames +@c other than as a deletion and an addition. + The normal way to move a file is to copy @var{old} to @var{new}, and then issue the normal @sc{cvs} commands to remove @var{old} from the repository, and add -@var{new} to it. (Both @var{old} and @var{new} could -contain relative paths, for example @file{foo/bar.c}). +@var{new} to it. +@c The following sentence is not true: one must cd into +@c the directory to run "cvs add". +@c (Both @var{old} and @var{new} could +@c contain relative paths, for example @file{foo/bar.c}). @example $ mv @var{old} @var{new} @@ -3208,8 +5111,9 @@ portion of the history you are accessing. For example, time of the rename. When @var{new} is committed its revision numbers will -start at 1.0 again, so if that bothers you, use the -@samp{-r rev} option to commit (@pxref{commit options}) +start again, usually at 1.1, so if that bothers you, +use the @samp{-r rev} option to commit. For more +information see @ref{Assigning revisions}. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Inside @@ -3271,9 +5175,9 @@ $ cvs remove @var{old} $ cvs commit @var{old} # @r{Remove all tags from @var{new}} $ cvs update @var{new} -$ cvs log @var{new} # @r{Remember the tag names} -$ cvs tag -d @var{tag1} -$ cvs tag -d @var{tag2} +$ cvs log @var{new} # @r{Remember the non-branch tag names} +$ cvs tag -d @var{tag1} @var{new} +$ cvs tag -d @var{tag2} @var{new} @dots{} @end example @@ -3305,10 +5209,18 @@ Disadvantages: @item You cannot easily see the history of the file across the rename. +@ignore +@c Is this true? I don't see how the revision numbers +@c _could_ start over, when new,v is just old,v with +@c the tags deleted. +@c If there is some need to reinstate this text, +@c it is "usually 1.1", not "1.0" and it needs an +@c xref to Assigning revisions @item Unless you use the @samp{-r rev} (@pxref{commit options}) flag when @var{new} is committed its revision numbers will start at 1.0 again. +@end ignore @end itemize @c --------------------------------------------------------------------- @@ -3318,16 +5230,14 @@ numbers will start at 1.0 again. @cindex Renaming directories @cindex Directories, moving -If you want to be able to retrieve old versions of the -module, you must move each file in the directory -with the @sc{cvs} commands. @xref{Outside}. The old, empty -directory will remain inside the repository, but it -will not appear in your workspace when you check out -the module in the future. -@c -- rephrase +The normal way to rename or move a directory is to +rename or move each file within it as described in +@ref{Outside}. Then check out with the @samp{-P} +option, as described in @ref{Removing directories}. -If you really want to rename or delete a directory, you -can do it like this: +If you really want to hack the repository to rename or +delete a directory in the repository, you can do it +like this: @enumerate @item @@ -3430,6 +5340,8 @@ history---what files have changed when, how, and by whom, there are a variety of mechanisms for looking through the history. +@c FIXME: should also be talking about how you look at +@c old revisions (e.g. "cvs update -p -r 1.2 foo.c"). @menu * log messages:: Log messages * history database:: The history database @@ -3463,6 +5375,63 @@ You can use the history file (@pxref{history file}) to log various @sc{cvs} actions. To retrieve the information from the history file, use the @code{cvs history} command (@pxref{history}). +@c +@c The history database has many problems: +@c * It is very unclear what field means what. This +@c could be improved greatly by better documentation, +@c but there are still non-orthogonalities (for +@c example, tag does not record the "repository" +@c field but most records do). +@c * Confusion about files, directories, and modules. +@c Some commands record one, some record others. +@c * File removal is not logged. There is an 'R' +@c record type documented, but CVS never uses it. +@c * Tags are only logged for the "cvs rtag" command, +@c not "cvs tag". The fix for this is not completely +@c clear (see above about modules vs. files). +@c * Are there other cases of operations that are not +@c logged? One would hope for all changes to the +@c repository to be logged somehow (particularly +@c operations like tagging, "cvs admin -k", and other +@c operations which do not record a history that one +@c can get with "cvs log"). Operations on the working +@c directory, like export, get, and release, are a +@c second category also covered by the current "cvs +@c history". +@c * The history file does not record the options given +@c to a command. The most serious manifestation of +@c this is perhaps that it doesn't record whether a command +@c was recursive. It is not clear to me whether one +@c wants to log at a level very close to the command +@c line, as a sort of way of logging each command +@c (more or less), or whether one wants +@c to log more at the level of what was changed (or +@c something in between), but either way the current +@c information has pretty big gaps. +@c * Further details about a tag--like whether it is a +@c branch tag or, if a non-branch tag, which branch it +@c is on. One can find out this information about the +@c tag as it exists _now_, but if the tag has been +@c moved, one doesn't know what it was like at the time +@c the history record was written. +@c * Whether operating on a particular tag, date, or +@c options was implicit (sticky) or explicit. +@c +@c Another item, only somewhat related to the above, is a +@c way to control what is logged in the history file. +@c This is probably the only good way to handle +@c different people having different ideas about +@c information/space tradeoffs. +@c +@c It isn't really clear that it makes sense to try to +@c patch up the history file format as it exists now to +@c include all that stuff. It might be better to +@c design a whole new CVSROOT/nhistory file and "cvs +@c nhistory" command, or some such, or in some other +@c way trying to come up with a clean break from the +@c past, which can address the above concerns. Another +@c open question is how/whether this relates to +@c taginfo/loginfo/etc. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node user-defined logging @@ -3517,13 +5486,11 @@ aborted. @section Annotate command @cindex annotate (subcommand) -@deffn Command {cvs annotate} [@code{-l}] files @dots{} +@deffn Command {cvs annotate} [@code{-flR}] [@code{-r rev}|@code{-D date}] files @dots{} For each file in @var{files}, print the head revision of the trunk, together with information on the last -modification for each line. The @code{-l} option means -to process the local directory only, not to recurse -(@pxref{Common options}). For example: +modification for each line. For example: @example $ cvs annotate ssfile @@ -3544,6 +5511,16 @@ or replaced; you need to use @code{cvs diff} for that @end deffn +The options to @code{cvs annotate} are listed in +@ref{Invoking CVS}, and can be used to select the files +and revisions to annotate. The options are described +in more detail in @ref{Common options}. + +@c FIXME: maybe an example using the options? Just +@c what it means to select a revision might be worth a +@c few words of explanation ("you want to see who +@c changed this line *before* 1.4"...). + @c --------------------------------------------------------------------- @node Keyword substitution @chapter Keyword substitution @@ -3609,6 +5586,14 @@ will normally never be locked when you use @sc{cvs}. Same as @code{$@asis{Header}$}, except that the @sc{rcs} filename is without a path. +@cindex Name keyword +@item $@asis{Name}$ +Tag name used to check out this file. +@c FIXME: should supply an example (e.g. "if you use +@c "cvs update -r foo" then Name expands to "foo"). Also +@c should add Name to testsuite (best way to ensure +@c that the example is correct!) + @cindex Locker keyword @item $@asis{Locker}$ The login name of the user who locked the revision @@ -3856,6 +5841,10 @@ contain data which looks like a keyword (@pxref{Keyword substitution}), so keyword expansion must be turned off. +@c FIXME: the third is that one can't do merges with +@c binary files. xref to Multiple Developers and the +@c reserved checkout issues. + The @samp{-kb} option available with some @sc{cvs} commands insures that neither line ending conversion nor keyword expansion will be done. If you are using @@ -3885,6 +5874,7 @@ $ cvs add -m"A test file" kotest $ cvs ci -m"First checkin; contains a keyword" kotest $ cvs admin -kb kotest $ cvs update -A kotest +$ cvs commit -m "make it binary" kotest # @r{For non-unix systems} @end example When you check in the file @file{kotest} the keywords @@ -3894,7 +5884,213 @@ admin -kb} command sets the default keyword substitution method for this file, but it does not alter the working copy of the file that you have. The easiest way to get the unexpanded version of -@file{kotest} is @code{cvs update -A}. +@file{kotest} is @code{cvs update -A}. If you need to +cope with line endings (that is, you are using a +@sc{cvs} client on a non-unix system), then you need to +check in a new copy of the file, as shown by the +@code{cvs commit} command above. +@c FIXME: should also describe what the *other users* +@c need to do, if they have checked out copies which +@c have been corrupted by lack of -kb. I think maybe +@c "cvs update -kb" or "cvs +@c update -A" would suffice, although the user who +@c reported this suggested removing the file, manually +@c removing it from CVS/Entries, and then "cvs update" + +However, in using @code{cvs admin -k} to change the +keyword expansion, be aware that the keyword expansion +mode is not version controlled. This means that, for +example, that if you have a text file in old releases, +and a binary file with the same name in new releases, +@sc{cvs} provides no way to check out the file in text +or binary mode depending on what version you are +checking out. There is no good workaround for this +problem. + +You can also set a default for whether @code{cvs add} +and @code{cvs import} treat a file as binary based on +its name; for example you could say that files who +names end in @samp{.exe} are binary. @xref{Wrappers}. +There is currently no way to have @sc{cvs} detect +whether a file is binary based on its contents. The +main difficulty with designing such a feature is that +it is not clear how to distinguish between binary and +non-binary files, and the rules to apply would vary +considerably with the operating system. +@c For example, it would be good on MS-DOS-family OSes +@c for anything containing ^Z to be binary. Having +@c characters with the 8th bit set imply binary is almost +@c surely a bad idea in the context of ISO-8859-* and +@c other such character sets. On VMS or the Mac, we +@c could use the OS's file typing. This is a +@c commonly-desired feature, and something of this sort +@c may make sense. But there are a lot of pitfalls here. + +@c I'm not sure about the best location for this. In +@c one sense, it might belong right after we've introduced +@c CVS's basic version control model, because people need +@c to figure out builds right away. The current location +@c is based on the theory that it kind of akin to the +@c "Revision management" section. +@node Builds +@chapter How your build system interacts with CVS +@cindex builds +@cindex make + +As mentioned in the introduction, @sc{cvs} does not +contain software for building your software from source +code. This section describes how various aspects of +your build system might interact with @sc{cvs}. + +@c Is there a way to discuss this without reference to +@c tools other than CVS? I'm not sure there is; I +@c wouldn't think that people who learn CVS first would +@c even have this concern. +One common question, especially from people who are +accustomed to @sc{rcs}, is how to make their build get +an up to date copy of the sources. The answer to this +with @sc{cvs} is two-fold. First of all, since +@sc{cvs} itself can recurse through directories, there +is no need to modify your @file{Makefile} (or whatever +configuration file your build tool uses) to make sure +each file is up to date. Instead, just use two +commands, first @code{cvs -q update} and then +@code{make} or whatever the command is to invoke your +build tool. Secondly, you do not necessarily +@emph{want} to get a copy of a change someone else made +until you have finished your own work. One suggested +approach is to first update your sources, then +implement, build and +test the change you were thinking of, and then commit +your sources (updating first if necessary). By +periodically (in between changes, using the approach +just described) updating your entire tree, you ensure +that your sources are sufficiently up to date. + +@cindex bill of materials +One common need is to record which versions of which +source files went into a particular build. This kind +of functionality is sometimes called @dfn{bill of +materials} or something similar. The best way to do +this with @sc{cvs} is to use the @code{tag} command to +record which versions went into a given build +(@pxref{Tags}). + +Using @sc{cvs} in the most straightforward manner +possible, each developer will have a copy of the entire +source tree which is used in a particular build. If +the source tree is small, or if developers are +geographically dispersed, this is the preferred +solution. In fact one approach for larger projects is +to break a project down into smaller +@c I say subsystem instead of module because they may or +@c may not use the modules file. +separately-compiled subsystems, and arrange a way of +releasing them internally so that each developer need +check out only those subsystems which are they are +actively working on. + +Another approach is to set up a structure which allows +developers to have their own copies of some files, and +for other files to access source files from a central +location. Many people have come up with some such a +@c two such people are paul@sander.cupertino.ca.us (for +@c a previous employer) +@c and gtornblo@senet.abb.se (spicm and related tools), +@c but as far as I know +@c noone has nicely packaged or released such a system (or +@c instructions for constructing one). +system using features such as the symbolic link feature +found in many operating systems, or the @code{VPATH} +feature found in many versions of @code{make}. One build +tool which is designed to help with this kind of thing +is Odin (see +@code{ftp://ftp.cs.colorado.edu/pub/distribs/odin}). +@c Should we be saying more about Odin? Or how you use +@c it with CVS? Also, the Prime Time Freeware for Unix +@c disk (see http://www.ptf.com/) has Odin (with a nice +@c paragraph summarizing it on the web), so that might be a +@c semi-"official" place to point people. +@c +@c Of course, many non-CVS systems have this kind of +@c functionality, for example OSF's ODE +@c (http://www.osf.org/ode/) or mk +@c (http://www.io.org/~pzi/heading.html; +@c ftp://ftp.interlog.com/pub/unix/mk is out of date). But I'm not sure +@c there is any point in mentioning them here unless they +@c can work with CVS. + +@node Compatibility +@chapter Compatibility between CVS Versions + +@cindex CVS, versions of +@cindex versions, of CVS +@cindex compatibility, between CVS versions +@c We don't mention versions older than CVS 1.3 +@c on the theory that it would clutter it up for the vast +@c majority of people, who don't have anything that old. +@c +The repository format is compatible going back to +@sc{cvs} 1.3. But see @ref{Watches Compatibility}, if +you have copies of @sc{cvs} 1.6 or older and you want +to use the optional developer communication features. +@c If you "cvs rm" and commit using 1.3, then you'll +@c want to run "rcs -sdead <file,v>" on each of the +@c files in the Attic if you then want 1.5 and +@c later to recognize those files as dead (I think the +@c symptom if this is not done is that files reappear +@c in joins). (Wait: the above will work but really to +@c be strictly correct we should suggest checking +@c in a new revision rather than just changing the +@c state of the head revision, shouldn't we?). +@c The old convert.sh script was for this, but it never +@c did get updated to reflect use of the RCS "dead" +@c state. +@c Note: this is tricky to document without confusing +@c people--need to carefully say what CVS version we +@c are talking about and keep in mind the distinction +@c between a +@c repository created with 1.3 and on which one now +@c uses 1.5+, and a repository on which one wants to +@c use both versions side by side (e.g. during a +@c transition period). +@c We might want to separate out the 1.3 compatibility +@c section (for repository & working directory) from the +@c rest--that might help avoid confusing people who +@c are upgrading (for example) from 1.6 to 1.8. +@c +@c A minor incompatibility is if a current version of CVS +@c puts "Nfoo" into CVS/Tag, then CVS 1.9 or older will +@c see this as if there is no tag. Seems to me this is +@c too obscure to mention. + +The working directory format is compatible going back +to @sc{cvs} 1.5. It did change between @sc{cvs} 1.3 +and @sc{cvs} 1.5. If you run @sc{cvs} 1.5 or newer on +a working directory checked out with @sc{cvs} 1.3, +@sc{cvs} will convert it, but to go back to @sc{cvs} +1.3 you need to check out a new working directory with +@sc{cvs} 1.3. + +The remote protocol is interoperable going back to @sc{cvs} 1.5, but no +further (1.5 was the first official release with the remote protocol, +but some older versions might still be floating around). In many +cases you need to upgrade both the client and the server to take +advantage of new features and bugfixes, however. + +@c Perhaps should be saying something here about the +@c "D" lines in Entries (written by CVS 1.9; 1.8 and +@c older don't use them). These are supposed to be +@c compatible in both directions, but I'm not sure +@c they quite are 100%. One common gripe is if you +@c "rm -r" a directory and 1.9 gets confused, as it +@c still sees it in Entries. That one is fixed in +@c (say) 1.9.6. Someone else reported problems with +@c starting with a directory which was checked out with +@c an old version, and then using a new version, and +@c some "D" lines appeared, but not for every +@c directory, causing some directories to be skipped. +@c They weren't sure how to reproduce this, though. @c --------------------------------------------------------------------- @node Revision management @@ -3949,35 +6145,35 @@ too regimented and thus counter-productive to the real goal, which is to get software written. @c --------------------------------------------------------------------- -@node Invoking CVS -@appendix Reference manual for CVS commands -@cindex Command reference -@cindex Reference, commands -@cindex Invoking CVS - -This appendix describes how to invoke @sc{cvs}, and -describes in detail those subcommands of @sc{cvs} which -are not fully described elsewhere. To look up a -particular subcommand, see @ref{Index}. +@node CVS commands +@appendix Guide to CVS commands + +This appendix describes the overall structure of +@sc{cvs} commands, and describes some commands in +detail (others are described elsewhere; for a quick +reference to @sc{cvs} commands, @pxref{Invoking CVS}). +@c The idea is that we want to move the commands which +@c are described here into the main body of the manual, +@c in the process reorganizing the manual to be +@c organized around what the user wants to do, not +@c organized around CVS commands. @menu * Structure:: Overall structure of CVS commands * ~/.cvsrc:: Default options with the ~/.csvrc file * Global options:: Options you give to the left of cvs_command * Common options:: Options you give to the right of cvs_command -* add:: Add a new file/directory to the repository * admin:: Administration front end for rcs * checkout:: Checkout sources for editing * commit:: Check files into the repository -* diff:: Run diffs between revisions +* diff:: Show differences between revisions * export:: Export sources from CVS, similar to checkout * history:: Show status of files and users * import:: Import sources into CVS, using vendor branches -* log:: Print out 'rlog' information for files +* log:: Show log messages for files * rdiff:: 'patch' format diffs between releases * release:: Indicate that a Module is no longer in use * rtag:: Add a tag to a module -* status:: Status info on the revisions * tag:: Add a tag to checked out version * update:: Bring work tree in sync with repository @end menu @@ -3990,10 +6186,7 @@ particular subcommand, see @ref{Index}. @cindex Command structure @cindex Format of CVS commands -The first release of @sc{cvs} consisted of a number of shell-scripts. -Today @sc{cvs} is implemented as a single program that is a front-end -to @sc{rcs} and @code{diff}. The overall format of all -@sc{cvs} commands is: +The overall format of all @sc{cvs} commands is: @example cvs [ cvs_options ] cvs_command [ command_options ] [ command_args ] @@ -4001,7 +6194,7 @@ cvs [ cvs_options ] cvs_command [ command_options ] [ command_args ] @table @code @item cvs -The program that is a front-end to @sc{rcs}. +The name of the @sc{cvs} program. @item cvs_options Some options that affect all sub-commands of @sc{cvs}. These are @@ -4113,6 +6306,14 @@ located. Overrides the setting of the @code{$RCSBIN} environment variable and any precompiled directory. This parameter should be specified as an absolute pathname. +@cindex TMPDIR, overriding +@cindex Overriding TMPDIR +@item -T @var{tempdir} +Use @var{tempdir} as the directory where temporary files are +located. Overrides the setting of the @code{$TMPDIR} environment +variable and any precompiled directory. This parameter should be +specified as an absolute pathname. + @cindex CVSROOT, overriding @cindex Overriding CVSROOT @item -d @var{cvs_root_directory} @@ -4124,7 +6325,9 @@ the @code{$CVSROOT} environment variable. @xref{Repository}. @cindex Overriding EDITOR @item -e @var{editor} Use @var{editor} to enter revision log information. Overrides the -setting of the @code{$CVSEDITOR} and @code{$EDITOR} environment variables. +setting of the @code{$CVSEDITOR} and @code{$EDITOR} +environment variables. For more information, see +@ref{Committing your changes}. @item -f Do not read the @file{~/.cvsrc} file. This @@ -4133,16 +6336,19 @@ non-orthogonality of the @sc{cvs} option set. For example, the @samp{cvs log} option @samp{-N} (turn off display of tag names) does not have a corresponding option to turn the display on. So if you have -@samp{-N} in the @file{~/.cvsrc} entry for @samp{diff}, +@samp{-N} in the @file{~/.cvsrc} entry for @samp{log}, you may need to use @samp{-f} to show the tag names. -@footnote{Yes, this really should be fixed, and it's -being worked on} @item -H +@itemx --help Display usage information about the specified @samp{cvs_command} (but do not actually execute the command). If you don't specify -a command name, @samp{cvs -H} displays a summary of all the -commands available. +a command name, @samp{cvs -H} displays overall help for +@sc{cvs}, including a list of other help options. +@c It seems to me it is better to document it this way +@c rather than trying to update this documentation +@c every time that we add a --help-foo option. But +@c perhaps that is confusing... @item -l Do not log the cvs_command in the command history (but execute it @@ -4163,7 +6369,7 @@ Cause the command to be somewhat quiet; informational messages, such as reports of recursion through subdirectories, are suppressed. -@cindex Read-only files +@cindex read-only files, and -r @item -r Make new working files files read-only. Same effect as if the @code{$CVSREAD} environment variable is set @@ -4181,6 +6387,7 @@ Trace program execution; display messages showing the steps of potential impact of an unfamiliar command. @item -v +@item --version Display version and copyright information for @sc{cvs}. @cindex CVSREAD, overriding @@ -4191,6 +6398,15 @@ setting of the @code{$CVSREAD} environment variable. Files are created read-write by default, unless @code{$CVSREAD} is set or @samp{-r} is given. +@item -x +Encrypt all communication between the client and the +server. Only has an effect on the @sc{cvs} client. As +of this writing, this is only implemented when using a +Kerberos connection (@pxref{Kerberos authenticated}). +Encryption support is not available by default; it must +be enabled using a special configure option, +@file{--enable-encryption}, when you build @sc{cvs}. + @item -z @var{gzip-level} Set the compression level. Only has an effect on the @sc{cvs} client. @@ -4233,30 +6449,152 @@ file using @samp{-D}, @sc{cvs} records the date you specified, so that further updates in the same directory will use the same date (for more information on sticky tags/dates, @pxref{Sticky tags}). -A wide variety of date formats are supported by the underlying -@sc{rcs} facilities, similar to those described in co(1), but not -exactly the same. The @var{date_spec} is interpreted as being -in the local timezone, unless a specific timezone is specified. -Examples of valid date specifications include: - -@example - 1 month ago - 2 hours ago - 400000 seconds ago - last year - last Monday - yesterday - a fortnight ago - 3/31/92 10:00:07 PST - January 23, 1987 10:05pm - 22:00 GMT -@end example - @samp{-D} is available with the @code{checkout}, @code{diff}, @code{export}, @code{history}, @code{rdiff}, @code{rtag}, and @code{update} commands. (The @code{history} command uses this option in a -slightly different way; @pxref{history options}). +slightly different way; @pxref{history options}). + +@c What other formats should we accept? I don't want +@c to start accepting a whole mess of non-standard +@c new formats (there are a lot which are in wide use in +@c one context or another), but practicality does +@c dictate some level of flexibility. +@c * POSIX.2 (e.g. touch, ls output, date) and other +@c POSIX and/or de facto unix standards (e.g. at). The +@c practice here is too inconsistent to be of any use. +@c * VMS dates. This is not a formal standard, but +@c there is a published specification (see SYS$ASCTIM +@c and SYS$BINTIM in the _VMS System Services Reference +@c Manual_), it is implemented consistently in VMS +@c utilities, and VMS users will expect CVS running on +@c VMS to support this format (and if we're going to do +@c that, better to make CVS support it on all +@c platforms. Maybe). +@c +@c NOTE: The tar manual has some documentation for +@c getdate.y (just for our info; we don't want to +@c attempt to document all the formats accepted by +@c getdate.y). +@c +@c One more note: In output, CVS should consistently +@c use one date format, and that format should be one that +@c it accepts in input as well. The former isn't +@c really true (see survey below), and I'm not +@c sure that either of those formats is accepted in +@c input. +@c +@c cvs log +@c current 1996/01/02 13:45:31 +@c Internet 02 Jan 1996 13:45:31 UT +@c ISO 1996-01-02 13:45:31 +@c cvs ann +@c current 02-Jan-96 +@c Internet-like 02 Jan 96 +@c ISO 96-01-02 +@c cvs status +@c current Tue Jun 11 02:54:53 1996 +@c Internet [Tue,] 11 Jun 1996 02:54:53 +@c ISO 1996-06-11 02:54:53 +@c note: date possibly should be omitted entirely for +@c other reasons. +@c cvs editors +@c current Tue Jun 11 02:54:53 1996 GMT +@c cvs history +@c current 06/11 02:54 +0000 +@c any others? +@c There is a good chance the proper solution has to +@c involve at least some level of letting the user +@c decide which format (with the default being the +@c formats CVS has always used; changing these might be +@c _very_ disruptive since scripts may very well be +@c parsing them). +@c +@c Another random bit of prior art concerning dates is +@c the strptime function which takes templates such as +@c "%m/%d/%y", and apparent a variant of getdate() +@c which also honors them. See +@c X/Open CAE Specification, System Interfaces and +@c Headers Issue 4, Version 2 (September 1994), in the +@c entry for getdate() on page 231 + +@cindex timezone, in input +@cindex zone, time, in input +A wide variety of date formats are supported by +@sc{cvs}. The most standard ones are ISO8601 (from the +International Standards Organization) and the Internet +e-mail standard (specified in RFC822 as amended by +RFC1123). + +@c Probably should be doing more to spell out just what +@c the rules are, rather than just giving examples. +@c But I want to keep this simple too. +@c So I don't know.... +@c A few specific issues: (1) Maybe should reassure +@c people that years after 2000 +@c work (they are in the testsuite, so they do indeed +@c work). (2) What do two digit years +@c mean? Where do we accept them? (3) Local times can +@c be ambiguous or nonexistent if they fall during the +@c hour when daylight savings time goes into or out of +@c effect. Pretty obscure, so I'm not at all sure we +@c should be documenting the behavior in that case. +ISO8601 dates have many variants but a few examples +are: + +@example +1972-09-24 +1972-09-24 20:05 +@end example +@c I doubt we really accept all ISO8601 format dates +@c (for example, decimal hours like 1972-09-24 20,2) +@c I'm not sure we should, many of them are pretty +@c bizarre and it has lots of gratuitous multiple ways +@c to specify the same thing. + +For more details about ISO8601 dates, see: + +@example +http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html +@end example +@c Perhaps we want to also cite other sources in +@c case that page goes away. For example: +@c http://www.saqqara.demon.co.uk/datefmt.htm + +In addition to the dates allowed in Internet e-mail +itself, @sc{cvs} also allows some of the fields to be +omitted. For example: +@c FIXME: Need to figure out better, and document, +@c what we want to allow the user to omit. +@c NOTE: "omit" does not imply "reorder". +@c FIXME: Need to cite a web page describing how to get +@c RFC's. + +@example +24 Sep 1972 20:05 +24 Sep +@end example + +The date is interpreted as being in the +local timezone, unless a specific timezone is +specified. + +These two date formats are preferred. However, +@sc{cvs} currently accepts a wide variety of other date +formats. They are intentionally not documented here in +any detail, and future versions of @sc{cvs} might not +accept all of them. +@c Maybe at +@c some point have CVS start give warnings on "unofficial" +@c formats (many of which might be typos or user +@c misunderstandings, and/or formats people never/rarely +@c use to specify dates)? + +One such format is +@code{@var{month}/@var{day}/@var{year}}. This may +confuse people who are accustomed to having the month +and day in the other order; @samp{1/4/96} is January 4, +not April 1. Remember to quote the argument to the @samp{-D} flag so that your shell doesn't interpret spaces as @@ -4277,17 +6615,14 @@ tag or date. (The most recent revision of the file will be used). @need 800 -@samp{-f} is available with these commands: @code{checkout}, -@code{export}, @code{rdiff}, @code{rtag}, and @code{update}. +@samp{-f} is available with these commands: +@code{annotate}, @code{checkout}, @code{export}, +@code{rdiff}, @code{rtag}, and @code{update}. @strong{Warning:} The @code{commit} command also has a @samp{-f} option, but it has a different behavior for that command. @xref{commit options}. -@item -H -Help; describe the options available for this command. This is -the only option supported for all @sc{cvs} commands. - @item -k @var{kflag} Alter the default @sc{rcs} processing of keywords. @xref{Keyword substitution}, for the meaning of @@ -4300,7 +6635,7 @@ file, and continues to use it with future update commands on the same file until you specify otherwise. The @samp{-k} option is available with the @code{add}, -@code{checkout}, @code{diff} and +@code{checkout}, @code{diff}, @code{import} and @code{update} commands. @item -l @@ -4311,10 +6646,11 @@ recursing through subdirectories. as the overall @samp{cvs -l} option, which you can specify to the left of a cvs command! -Available with the following commands: @code{checkout}, -@code{commit}, @code{diff}, @code{export}, @code{log}, -@code{remove}, @code{rdiff}, @code{rtag}, -@code{status}, @code{tag}, and @code{update}. +Available with the following commands: @code{annotate}, @code{checkout}, +@code{commit}, @code{diff}, @code{edit}, @code{editors}, @code{export}, +@code{log}, @code{rdiff}, @code{remove}, @code{rtag}, +@code{status}, @code{tag}, @code{unedit}, @code{update}, @code{watch}, +and @code{watchers}. @cindex Editor, avoiding invocation of @cindex Avoiding editor invocation @@ -4337,28 +6673,21 @@ Available with the @code{checkout}, @code{commit}, @code{export}, and @code{rtag} commands. @item -P -Prune (remove) directories that are empty after being updated, on -@code{checkout}, or @code{update}. Normally, an empty directory -(one that is void of revision-controlled files) is left alone. -Specifying @samp{-P} will cause these directories to be silently -removed from your checked-out sources. This does not remove the -directory from the repository, only from your checked out copy. -Note that this option is implied by the @samp{-r} or @samp{-D} -options of @code{checkout} and @code{export}. -@c -- implied-- +Prune empty directories. See @xref{Removing directories}. @item -p Pipe the files retrieved from the repository to standard output, rather than writing them in the current directory. Available with the @code{checkout} and @code{update} commands. -@item -W -Specify file names that should be filtered. You can -use this option repeatedly. The spec can be a file -name pattern of the same type that you can specify in -the @file{.cvswrappers} file. -Avaliable with the following commands: @code{import}, -and @code{update}. +@item -R +Process directories recursively. This is on by default. + +Available with the following commands: @code{annotate}, @code{checkout}, +@code{commit}, @code{diff}, @code{edit}, @code{editors}, @code{export}, +@code{rdiff}, @code{remove}, @code{rtag}, +@code{status}, @code{tag}, @code{unedit}, @code{update}, @code{watch}, +and @code{watchers}. @item -r @var{tag} Use the revision specified by the @var{tag} argument instead of the @@ -4368,7 +6697,28 @@ always available: @samp{HEAD} refers to the most recent version available in the repository, and @samp{BASE} refers to the revision you last checked out into the current working directory. -The tag specification is sticky when you use this option +@c FIXME: What does HEAD really mean? I believe that +@c the current answer is the head of the default branch +@c for all cvs commands except diff. For diff, it +@c seems to be (a) the head of the trunk (or the default +@c branch?) if there is no sticky tag, (b) the head of the +@c branch if there is a branch sticky tag, and (c) the +@c same as BASE if there is a non-branch sticky tag. (c) +@c would appear to be strange, maybe accidental, and so there would +@c presumably be +@c little problem changing it. (b) is ugly as it differs +@c from what HEAD means for other commands, but people +@c might be used to it (note a change in NEWS? Or provide +@c advance warning of it changing?) and possible useful +@c (could be fixed by a new tag ".bhead" which would mean +@c the head of the appropriate branch). This +@c should be investigated, test cases written, and +@c documented (but HEAD should mean the same thing for all +@c CVS commands, so I don't know if we should be +@c documenting the current "cvs diff" behavior). + +The tag specification is sticky when you use this +@c option with @code{checkout} or @code{update} to make your own copy of a file: @sc{cvs} remembers the tag and continues to use it on future update commands, until you specify otherwise (for more information @@ -4388,128 +6738,16 @@ which you can specify to the left of a cvs command! @code{diff}, @code{history}, @code{export}, @code{rdiff}, @code{rtag}, and @code{update} commands. -@end table - -@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@node add -@appendixsec add---Add a new file/directory to the repository -@cindex Add (subcommand) - -@itemize @bullet -@item -Synopsis: add [-k kflag] [-m 'message'] files@dots{} -@item -Requires: repository, working directory. -@item -Changes: working directory. -@item -Synonym: new -@end itemize - -Use the @code{add} command to create a new file or directory in the -source repository. The files or directories specified with @code{add} -must already exist in the current directory (which must have been -created with the @code{checkout} command). To add a whole new directory -hierarchy to the source repository (for example, files received -from a third-party vendor), use the @code{import} command -instead. @xref{import}. - -If the argument to @code{add} refers to an immediate -sub-directory, the directory is created at the correct place in -the source repository, and the necessary @sc{cvs} administration -files are created in your working directory. If the directory -already exists in the source repository, @code{add} still creates -the administration files in your version of the directory. -This allows you to use @code{add} to add a particular directory -to your private sources even if someone else created that -directory after your checkout of the sources. You can do the -following: - -@example -$ mkdir new_directory -$ cvs add new_directory -$ cvs update new_directory -@end example - -An alternate approach using @code{update} might be: - -@example -$ cvs update -d new_directory -@end example - -(To add any available new directories to your working directory, -it's probably simpler to use @code{checkout} (@pxref{checkout}) -or @samp{update -d} (@pxref{update})). - -The added files are not placed in the source repository until you -use @code{commit} to make the change permanent. Doing an -@code{add} on a file that was removed with the @code{remove} -command will resurrect the file, unless a @code{commit} command -intervened. -@xref{Removing files}, for an example. - - -Unlike most other commands @code{add} never recurses down -directories. It cannot yet handle relative paths. Instead of - -@example -$ cvs add foo/bar.c -@end example - -you have to do - -@example -$ cd foo -$ cvs add bar.c -@end example - -@menu -* add options:: add options -* add examples:: add examples -@end menu - -@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -@node add options -@appendixsubsec add options -@cindex Add options - -There are only two options you can give to @samp{add}: +@item -W +Specify file names that should be filtered. You can +use this option repeatedly. The spec can be a file +name pattern of the same type that you can specify in +the @file{.cvswrappers} file. +Avaliable with the following commands: @code{import}, +and @code{update}. -@table @code -@item -k @var{kflag} -This option specifies the default way that this file -will be checked out. The @var{kflag} argument -(@pxref{Substitution modes}) is stored in the @sc{rcs} -file and can be changed with @code{admin -k} -(@pxref{admin options}). See @ref{Binary files}, for -information on using this option for binary files. - -@item -m @var{description} -Using this option, you can give a description for the file. This -description appears in the history log (if it is enabled, -@pxref{history file}). It will also be saved in the @sc{rcs} history -file inside the repository when the file is committed. The -@code{log} command displays this description. - -The description can be changed using @samp{admin -t}. -@xref{admin}. - -If you omit the @samp{-m @var{description}} flag, an empty string will be -used. You will not be prompted for a description. @end table -@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -@node add examples -@appendixsubsec add examples - -To add the file @file{backend.c} to the repository, with a -description, the following can be used. - -@example -$ cvs add -m "Optimizer and code generation passes." backend.c -$ cvs commit -m "Early version. Not yet compilable." backend.c -@end example - @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node admin @appendixsec admin---Administration front end for rcs @@ -4530,6 +6768,9 @@ all its options and arguments to the @code{rcs} command; it does no filtering or other processing. This command @emph{does} work recursively, however, so extreme care should be used. +@c "group" should probably read "unix group" (but what +@c does NT local do?). "compiled in value" is +@c unclear--compiled in to what? If there is a group whose name matches a compiled in value which defaults to @code{cvsadmin}, only members of that group can use @code{cvs admin}. To disallow @@ -4551,7 +6792,7 @@ with @sc{cvs}. Some even makes it impossible to use This description of the available options is based on the @samp{rcs(1)} man page, but modified to suit -readers that are more interrested in @sc{cvs} than +readers that are more interested in @sc{cvs} than @sc{rcs}. @table @code @@ -4569,12 +6810,10 @@ login names appearing in the comma-separated list When used with bare @sc{rcs}, this option sets the default branch to @var{rev}; in @sc{cvs} sticky tags (@pxref{Sticky tags}) are a better -way to decide which branch you want to work on. With -@sc{cvs}, this option can be used to control behavior -with respect to the vendor branch. -@c FIXME: document how you use it with the vendor -@c branch (or fix cvs so that there is a more graceful -@c way to handle the case). +way to decide which branch you want to work on. There +is one use with @sc{cvs}: to revert to the vendor's +version when using vendor branches (@pxref{Reverting +local changes}). @item -c@var{string} Useful with @sc{cvs}. Sets the comment leader to @@ -4592,6 +6831,8 @@ names appearing in the comma-separated list @var{logins} from the access list of the RCS file. If @var{logins} is omitted, erase the entire access list. +@c FIXME: Doesn't work with client/server CVS; we +@c should probably just not accept the option. @item -I Run interactively, even if the standard input is not a terminal. @@ -4608,8 +6849,6 @@ substitution}. Giving an explicit @samp{-k} option to @code{cvs update}, @code{cvs export}, or @code{cvs checkout} overrides this default. -@cindex Reserved checkouts -@cindex RCS-style locking @item -l[@var{rev}] Lock the revision with number @var{rev}. If a branch is given, lock the latest revision on that branch. If @@ -4846,6 +7085,8 @@ collection of source directories and files, or paths to directories or files in the repository. The symbolic names are defined in the @samp{modules} file. @xref{modules}. +@c Needs an example, particularly of the non-"modules" +@c case but probably of both. Depending on the modules you specify, @code{checkout} may recursively create directories and populate them with @@ -4884,6 +7125,9 @@ to the @code{update} command, that is, any new directories that have been created in the repository will appear in your work area. @xref{update}. +For the output produced by the @code{checkout} command +see @ref{update output}. + @menu * checkout options:: checkout options * checkout examples:: checkout examples @@ -4914,7 +7158,8 @@ Process @sc{rcs} keywords according to @var{kflag}. See co(1). This option is sticky; future updates of this file in this working directory will use the same @var{kflag}. The @code{status} command can be viewed -to see the sticky options. @xref{status}. +to see the sticky options. See @ref{Invoking CVS}, for +more information on the @code{status} command. @item -l Local; run only in current working directory. @@ -4925,11 +7170,14 @@ with the @samp{-o} option in the modules file; @pxref{modules}). @item -P -Prune empty directories. +Prune empty directories. See @ref{Moving directories}. @item -p Pipe files to the standard output. +@item -R +Checkout directories recursively. This option is on by default. + @item -r @var{tag} Use revision @var{tag}. This option is sticky, and implies @samp{-P}. See @ref{Sticky tags}, for more information on sticky tags/dates. @@ -4948,11 +7196,18 @@ Copy the module file, sorted, to the standard output, instead of creating or modifying any files or directories in your working directory. +@c Should clarify whether dir can specify a +@c subdirectory (for example "foo/bar"). As of May, +@c 1996, it is said to work for local CVS if the parent +@c directories already exist, and not at all for remote +@c CVS. The remote CVS behavior at least seems like it +@c is clearly a bug. @item -d @var{dir} Create a directory called @var{dir} for the working files, instead of using the module name. Unless you also use @samp{-N}, the paths created under @var{dir} will be as short as possible. +@c FIXME: What the #$@!#$# does "short as possible" mean? @item -j @var{tag} With two @samp{-j} options, merge changes from the @@ -5014,12 +7269,8 @@ $ cvs checkout -D yesterday tc @itemize @bullet @item -Version 1.3 Synopsis: commit [-lnR] [-m 'log_message' | --f file] [-r revision] [files@dots{}] -@item -Version 1.3.1 Synopsis: commit [-lnRf] [-m 'log_message' | +Synopsis: commit [-lnRf] [-m 'log_message' | -F file] [-r revision] [files@dots{}] -@c -- rename-f-F-- @item Requires: working directory, repository. @item @@ -5028,11 +7279,6 @@ Changes: repository. Synonym: ci @end itemize -@strong{Warning:} The @samp{-f @var{file}} option will -probably be renamed to @samp{-F @var{file}}, and @samp{-f} -will be given a new behavior in future releases of @sc{cvs}. -@c -- rename-f-F-- - Use @code{commit} when you want to incorporate changes from your working source files into the source repository. @@ -5064,8 +7310,7 @@ repository. This log message can be retrieved with the @code{log} command; @xref{log}. You can specify the log message on the command line with the @samp{-m @var{message}} option, and thus avoid the editor invocation, -or use the @samp{-f @var{file}} option to specify -@c -- rename-f-F-- +or use the @samp{-F @var{file}} option to specify that the argument file contains the log message. @menu @@ -5094,22 +7339,21 @@ Commit directories recursively. This is on by default. @item -r @var{revision} Commit to @var{revision}. @var{revision} must be either a branch, or a revision on the main trunk that -is higher than any existing revision number. You +is higher than any existing revision number +(@pxref{Assigning revisions}). You cannot commit to a specific revision on a branch. +@c FIXME: Need xref for branch case. @end table @code{commit} also supports these options: @table @code @item -F @var{file} -This option is present in @sc{cvs} releases 1.3-s3 and -later. Read the log message from @var{file}, instead +Read the log message from @var{file}, instead of invoking an editor. @item -f -@c -- rename-f-F-- -This option is present in @sc{cvs} 1.3-s3 and later releases -of @sc{cvs}. Note that this is not the standard behavior of +Note that this is not the standard behavior of the @samp{-f} option as defined in @xref{Common options}. Force @sc{cvs} to commit a new revision even if you haven't @@ -5122,14 +7366,12 @@ $ cvs commit -f @var{file} $ cvs commit -r 1.8 @var{file} @end example -@item -f @var{file} -@c -- rename-f-F-- -This option is present in @sc{cvs} releases 1.3, 1.3-s1 and -1.3-s2. Note that this is not the standard behavior of -the @samp{-f} option as defined in @xref{Common options}. - -Read the log message from @var{file}, instead -of invoking an editor. +@c This is odd, but it's how CVS has worked for some +@c time. +The @samp{-f} option disables recursion (i.e., it +implies @samp{-l}). To force @sc{cvs} to commit a new +revision for all files in all subdirectories, you must +use @samp{-f -R}. @item -m @var{message} Use @var{message} as the log message, instead of @@ -5141,36 +7383,6 @@ invoking an editor. @node commit examples @appendixsubsec commit examples -@appendixsubsubsec New major release number - -When you make a major release of your product, you -might want the revision numbers to track your major -release number. You should normally not care about -the revision numbers, but this is a thing that many -people want to do, and it can be done without doing any -harm. - -To bring all your files up to the @sc{rcs} revision 3.0 -(including those that haven't changed), you might do: - -@example -$ cvs commit -r 3.0 -@end example - -Note that it is generally a bad idea to try to make the -@sc{rcs} revision number equal to the current release number -of your product. You should think of the revision -number as an internal number that the @sc{cvs} package -maintains, and that you generally never need to care -much about. Using the @code{tag} and @code{rtag} -commands you can give symbolic names to the releases -instead. @xref{tag} and @xref{rtag}. - -Note that the number you specify with @samp{-r} must be -larger than any existing revision number. That is, if -revision 3.0 exists, you cannot @samp{cvs commit --r 1.3}. - @appendixsubsubsec Committing to a branch You can commit to a branch revision (one that has an @@ -5249,12 +7461,12 @@ $ cvs checkout -r EXPR1 whatever_module @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node diff -@appendixsec diff---Run diffs between revisions +@appendixsec diff---Show differences between revisions @cindex Diff (subcommand) @itemize @bullet @item -Synopsis: diff [-l] [rcsdiff_options] [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files@dots{}] +Synopsis: diff [-lR] [rcsdiff_options] [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files@dots{}] @item Requires: working directory, repository. @item @@ -5292,14 +7504,6 @@ them): Use the most recent revision no later than @var{date}. See @samp{-r} for how this affects the comparison. -@sc{cvs} can be configured to pass the @samp{-D} option -through to @code{rcsdiff} (which in turn passes it on -to @code{diff}. @sc{Gnu} diff uses @samp{-D} as a way to -put @code{cpp}-style @samp{#define} statements around the output -differences. There is no way short of testing to -figure out how @sc{cvs} was configured. In the default -configuration @sc{cvs} will use the @samp{-D @var{date}} option. - @item -k @var{kflag} Process @sc{rcs} keywords according to @var{kflag}. See co(1). @@ -5323,18 +7527,72 @@ outcome in any way). One or both @samp{-r} options can be replaced by a @samp{-D @var{date}} option, described above. + +@item --ifdef=@var{arg} +Output in ifdef format. Consult the documentation of +your underlying diff program concerning the @samp{-D} +option to diff, for more information on this format. @end table -Any other options that are found are passed through to +@c FIXME? Probably should document -c here, and +@c perhaps arrange for CVS to support it via a diff library or +@c some such. Or perhaps figure that "all" diff +@c programs support -c? Ideas is to preserve the +@c ability to pass the buck to diff on all the hairy +@c stuff, while still providing at least one, and +@c perhaps several popular standard formats. But this +@c is all in the idea stage, and probably needs more +@c thought and refinement. -u might be similar, in +@c terms of being something that it might make sense to +@c document here. +@c FIXME: also should be a way to pass through +@c arbitrary options, so that the user can do +@c "--pass=-Z --pass=foo" or something even if CVS +@c doesn't know about the -Z option to diff. +@c Note on -N: The current CVS implementation does require that the +@c underlying diff supports -N so we can document it as +@c a pass-through even if the implementation details +@c are more complicated. +@c +@c FIXME? Reference to discussion of which diff CVS +@c uses (one in path, or....). +The following options are passed through to @code{rcsdiff}, which in turn passes them to @code{diff}. The exact meaning of the options depends -on which @code{diff} you are using. The long options -introduced in @sc{gnu} diff 2.0 are not yet supported in -@sc{cvs}. See the documentation for your @code{diff} to see -which options are supported. +on which @code{diff} you are using. See the +documentation for your @code{diff} for details. + +@code{-a} @code{-b} @code{-B} @code{-c} @w{@code{-C} +@var{nlines}} @code{-d} @code{-e} @code{-f} @code{-h} +@code{-H} @code{-i} @code{-n} @code{-N} @code{-p} +@code{-s} @code{-t} @code{-u} @code{-U} @var{nlines} +@w{@code{-F} @var{regexp}} @w{@code{-I} @var{regexp}} +@w{@code{-L} @var{label}} @code{-T} @w{@code{-V} +@var{arg}} @w{@code{-W} @var{columns}} @code{-w} +@code{-y} @code{-0} @code{-1} @code{-2} @code{-3} +@code{-4} @code{-5} @code{-6} @code{-7} @code{-8} +@code{-9} @code{--binary} @code{--brief} +@code{--changed-group-format=@var{arg}} +@code{--context[=@var{lines}]} @code{--ed} +@code{--expand-tabs} @code{--forward-ed} +@code{--horizon-lines=@var{arg}} +@code{--ignore-all-space} @code{--ignore-blank-lines} +@code{--ignore-case} +@code{--ignore-matching-lines=@var{regexp}} +@code{--ignore-space-change} @code{--initial-tab} +@code{--label=@var{label}} @code{--left-column} +@code{--minimal} @code{--new-file} +@code{--new-line-format=@var{arg}} +@code{--old-line-format=@var{arg}} @code{--paginate} +@code{--rcs} @code{--report-identical-files} +@code{--code-c-function} @code{--side-by-side} +@code{--show-function-line=@var{regexp}} +@code{--speed-large-files} +@code{--suppress-common-lines} @code{--text} +@code{--unchanged-group-format=@var{arg}} +@code{--unified[=@var{lines}]} +@code{--width=@var{columns}} -@c -- Document some common useful diff options, such as -@c -u and -c. @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @node diff examples @appendixsubsec diff examples @@ -5380,7 +7638,7 @@ $ cvs diff -u | less @itemize @bullet @item -Synopsis: export [-flNn] [-r rev|-D date] [-k subst] [-d dir] module@dots{} +Synopsis: export [-flNnR] [-r rev|-D date] [-k subst] [-d dir] module@dots{} @item Requires: repository. @item @@ -5539,6 +7797,8 @@ Certain commands have a single record type: release @item O checkout +@item E +export @item T rtag @end table @@ -5638,6 +7898,8 @@ Contributed examples will gratefully be accepted. @appendixsec import---Import sources into CVS, using vendor branches @cindex Import (subcommand) +@c FIXME: This node is way too long for one which has subnodes. + @itemize @bullet @item Synopsis: import [-options] repository vendortag releasetag@dots{} @@ -5669,7 +7931,8 @@ you to do. If @sc{cvs} decides a file should be ignored (@pxref{cvsignore}), it does not import it and prints -@samp{I } followed by the filename +@samp{I } followed by the filename (@pxref{import output}, for a +complete description of the output). If the file @file{$CVSROOT/CVSROOT/cvswrappers} exists, any file whose names match the specifications in that @@ -5691,8 +7954,19 @@ branch (e.g., for 1.1.1). You must also specify at least one @var{releasetag} to identify the files at the leaves created each time you execute @code{import}. +@c I'm not completely sure this belongs here. But +@c we need to say it _somewhere_ reasonably obvious; it +@c is a common misconception among people first learning CVS +Note that @code{import} does @emph{not} change the +directory in which you invoke it. In particular, it +does not set up that directory as a @sc{cvs} working +directory; if you want to work with the sources import +them first and then check them out into a different +directory (@pxref{Getting the source}). + @menu * import options:: import options +* import output:: import output * import examples:: import examples @end menu @@ -5725,7 +7999,7 @@ in the future. Indicate the RCS keyword expansion mode desired. This setting will apply to all files created during the import, but not to any files that previously existed in -the repository. See @ref{Substitution modes} for a +the repository. See @ref{Substitution modes}, for a list of valid @samp{-k} settings. @item -I @var{name} @@ -5749,6 +8023,46 @@ file. @xref{Wrappers}. @end table @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node import output +@appendixsubsec import output + +@code{import} keeps you informed of its progress by printing a line +for each file, preceded by one character indicating the status of the file: + +@table @code +@item U @var{file} +The file already exists in the repository and has not been locally +modified; a new revision has been created (if necessary). + +@item N @var{file} +The file is a new file which has been added to the repository. + +@item C @var{file} +The file already exists in the repository but has been locally modified; +you will have to merge the changes. + +@item I @var{file} +The file is being ignored (@pxref{cvsignore}). + +@cindex symbolic link, importing +@cindex link, symbolic, importing +@c FIXME: also (somewhere else) probably +@c should be documenting what happens if you "cvs add" +@c a symbolic link. Also maybe what happens if +@c you manually create symbolic links within the +@c repository (? - not sure why we'd want to suggest +@c doing that). +@item L @var{file} +The file is a symbolic link; @code{cvs import} ignores symbolic links. +People periodically suggest that this behavior should +be changed, but if there is a consensus on what it +should be changed to, it doesn't seem to be apparent. +(Various options in the @file{modules} file can be used +to recreate symbolic links on checkout, update, etc.; +@pxref{modules}.) +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @node import examples @appendixsubsec import examples @@ -5756,31 +8070,51 @@ file. @xref{Wrappers}. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node log -@appendixsec log---Print out 'rlog' information for files +@appendixsec log---Print out log information for files @cindex Log (subcommand) @itemize @bullet @item -Synopsis: log [-l] rlog-options [files@dots{}] +Synopsis: log [options] [files@dots{}] @item Requires: repository, working directory. @item Changes: nothing. -@item -Synonym: rlog @end itemize -Display log information for files. @code{log} calls -the @sc{rcs} utility @code{rlog}, which prints all available -information about the @sc{rcs} history file. This includes -the location of the @sc{rcs} file, the @dfn{head} revision -(the latest revision on the trunk), all symbolic names (tags) -and some other things. For each revision, the revision -number, the author, the number of lines added/deleted and -the log message are printed. All times are displayed in -Coordinated Universal Time (UTC). (Other parts of @sc{cvs} -print times in the local timezone). -@c -- timezone-- +Display log information for files. @code{log} used to +call the @sc{rcs} utility @code{rlog}. Although this +is no longer true in the current sources, this history +determines the format of the output and the options, +which are not quite in the style of the other @sc{cvs} +commands. + +@cindex timezone, in output +@cindex zone, time, in output +@c Kind of a funny place to document the timezone used +@c in output from commands other than @code{log}. +@c There is also more we need to say about this, +@c including what happens in a client/server environment. +The output includes the location of the @sc{rcs} file, +the @dfn{head} revision (the latest revision on the +trunk), all symbolic names (tags) and some other +things. For each revision, the revision number, the +author, the number of lines added/deleted and the log +message are printed. All times are displayed in +Coordinated Universal Time (UTC). (Other parts of +@sc{cvs} print times in the local timezone). +@c FIXCVS: need a better way to control the timezone +@c used in output. Previous/current versions of CVS did/do +@c sometimes support -z in RCSINIT, and/or an +@c undocumented (except by reference to 'rlog') -z option +@c to cvs log, but this has not been a consistent, +@c documented feature. Perhaps a new global option, +@c where LT means the client's timezone, which the +@c client then communicates to the server, is the +@c right solution. + +@strong{Warning:} @code{log} uses @samp{-R} in a way that conflicts +with the normal use inside @sc{cvs} (@pxref{Common options}). @menu * log options:: log options @@ -5791,42 +8125,29 @@ print times in the local timezone). @node log options @appendixsubsec log options -Only one option is interpreted by @sc{cvs} and not passed on to @code{rlog}: - -@table @code -@item -l -Local; run only in current working directory. (Default -is to run recursively). -@end table - -By default, @code{rlog} prints all information that is -available. All other options (including those that -normally behave differently) are passed through to -@code{rlog} and restrict the output. See rlog(1) for a -complete description of options. This incomplete list -(which is a slightly edited extract from rlog(1)) lists -all options that are useful in conjunction with @sc{cvs}. - -@strong{Please note:} There can be no space between the option -and its argument, since @code{rlog} parses its options -in a different way than @sc{cvs}. +By default, @code{log} prints all information that is +available. All other options restrict the output. @table @code @item -b Print information about the revisions on the default branch, normally the highest branch on the trunk. -@item -d@var{dates} +@item -d @var{dates} Print information about revisions with a checkin date/time in the range given by the -semicolon-separated list of dates. The following table -explains the available range formats: +semicolon-separated list of dates. The date formats +accepted are those accepted by the @samp{-D} option to +many other @sc{cvs} commands (@pxref{Common options}). +Dates can be combined into ranges as follows: +@c Should we be thinking about accepting ISO8601 +@c ranges? For example "1972-09-10/1972-09-12". @table @code @item @var{d1}<@var{d2} @itemx @var{d2}>@var{d1} Select the revisions that were deposited between -@var{d1} and @var{d2} inclusive. +@var{d1} and @var{d2}. @item <@var{d} @itemx @var{d}> @@ -5841,16 +8162,21 @@ Select the single, latest revision dated @var{d} or earlier. @end table -The date/time strings @var{d}, @var{d1}, and @var{d2} -are in the free format explained in co(1). Quoting is -normally necessary, especially for < and >. Note that -the separator is a semicolon (;). +The @samp{>} or @samp{<} characters may be followed by +@samp{=} to indicate an inclusive range rather than an +exclusive one. + +Note that the separator is a semicolon (;). @item -h Print only the @sc{rcs} pathname, working pathname, head, default branch, access list, locks, symbolic names, and suffix. +@item -l +Local; run only in current working directory. (Default +is to run recursively). + @item -N Do not print the list of tags for this file. This option can be very useful when your site uses a lot of @@ -5882,10 +8208,7 @@ branch containing @var{rev}. @item @var{branch} An argument that is a branch means all revisions on -that branch. You can unfortunately not specify a -symbolic branch here. You must specify the numeric -branch number. @xref{Magic branch numbers}, for an -explanation. +that branch. @item @var{branch1}:@var{branch2} A range of branches means all revisions @@ -5897,8 +8220,10 @@ The latest revision in @var{branch}. A bare @samp{-r} with no revisions means the latest revision on the default branch, normally the trunk. +There can be no space between the @samp{-r} option and +its argument. -@item -s@var{states} +@item -s @var{states} Print information about revisions whose state attributes match one of the states given in the comma-separated list @var{states}. @@ -5910,13 +8235,14 @@ Print the same as @samp{-h}, plus the descriptive text. Print information about revisions checked in by users with login names appearing in the comma-separated list @var{logins}. If @var{logins} is omitted, the user's -login is assumed. +login is assumed. There can be no space between the +@samp{-w} option and its argument. @end table -@code{rlog} prints the intersection of the revisions -selected with the options @samp{-d}, @samp{-l}, -@samp{-s}, and @samp{-w}, intersected with the union of -the revisions selected by @samp{-b} and @samp{-r}. +@code{log} prints the intersection of the revisions +selected with the options @samp{-d}, @samp{-s}, and +@samp{-w}, intersected with the union of the revisions +selected by @samp{-b} and @samp{-r}. @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @node log examples @@ -5985,6 +8311,9 @@ recent revision (instead of ignoring the file). @item -l Local; don't descend subdirectories. +@item -R +Examine directories recursively. This option is on by default. + @item -r @var{tag} Use revision @var{tag}. @end table @@ -6087,7 +8416,7 @@ history log. @menu * release options:: release options -* release output:: release options +* release output:: release output * release examples:: release examples @end menu @@ -6103,12 +8432,12 @@ Delete your working copy of the file if the release succeeds. If this flag is not given your files will remain in your working directory. -@strong{Warning:} The @code{release} command uses -@samp{rm -r @file{module}} to delete your file. This +@strong{Warning:} The @code{release} command deletes +all directories and files recursively. This has the very serious side-effect that any directory that you have created inside your checked-out sources, and not added to the repository (using the @code{add} -command; @pxref{add}) will be silently deleted---even +command; @pxref{Adding files}) will be silently deleted---even if it is non-empty! @end table @@ -6122,15 +8451,18 @@ up-to-date. @strong{Warning:} Any new directories that you have created, but not added to the @sc{cvs} directory hierarchy -with the @code{add} command (@pxref{add}) will be +with the @code{add} command (@pxref{Adding files}) will be silently ignored (and deleted, if @samp{-d} is specified), even if they contain files. +@c FIXCVS: This is a bug. But is it true? I think +@c maybe they print "? dir" now. @table @code @item U @var{file} +@itemx P @var{file} There exists a newer revision of this file in the repository, and you have not modified your local copy -of the file. +of the file (@samp{U} and @samp{P} mean the same thing). @item A @var{file} The file has been added to your private copy of the @@ -6155,13 +8487,6 @@ not in the list of files for @sc{cvs} to ignore (see the description of the @samp{-I} option, and @pxref{cvsignore}). If you remove your working sources, this file will be lost. - -Note that no warning message like this is printed for -spurious directories that @sc{cvs} encounters. The -directory, and all its contents, are silently ignored. - -@c FIXME -- CVS should be fixed to print "? foo" for -@c such spurious directories @end table @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -6181,7 +8506,7 @@ $ @end example @node rtag -@appendixsec rtag---Add a tag to the RCS file +@appendixsec rtag---Add a symbolic tag to a module @cindex Rtag (subcommand) @itemize @bullet @@ -6242,7 +8567,7 @@ Do not run any tag program that was specified with the (@pxref{modules}). @item -R -Commit directories recursively. This is on by default. +Tag directories recursively. This is on by default. @item -r @var{tag} Only tag those files that contain @var{tag}. This can @@ -6256,15 +8581,17 @@ are available: @table @code @item -a +@c FIXME: What does this option mean in terms of user +@c concepts, not CVS internals? Use the @samp{-a} option to have @code{rtag} look in the -@file{Attic} (@pxref{Removing files}) for removed files +@file{Attic} (@pxref{Attic}) for removed files that contain the specified tag. The tag is removed from these files, which makes it convenient to re-use a symbolic tag as development continues (and files get removed from the up-coming distribution). @item -b -Make the tag a branch tag. @xref{Branches}. +Make the tag a branch tag. @xref{Revisions and branches}. @item -d Delete the tag instead of creating it. @@ -6286,69 +8613,8 @@ module). @end ignore @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@node status -@appendixsec status---Status info on the revisions -@cindex Status (subcommand) - -@itemize @bullet -@item -status [-lR] [-v] [files@dots{}] -@item -Requires: working directory, repository. -@item -Changes: nothing. -@end itemize - -Display a brief report on the current status of files -with respect to the source repository, including any -sticky tags, dates, or @samp{-k} options. - -You can also use this command to determine the -potential impact of a @samp{cvs update} on your working -source directory---but remember that things might -change in the repository before you run @code{update}. - -@menu -* status options:: status options -@end menu - -@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -@node status options -@appendixsubsec status options - -These standard options are supported by @code{status} -(@pxref{Common options}, for a complete description of -them): - -@table @code -@item -l -Local; run only in current working directory. - -@item -R -Commit directories recursively. This is on by default. -@end table - -There is one additional option: - -@table @code -@item -v -Verbose. In addition to the information normally -displayed, print all symbolic tags, together with the -numerical value of the revision or branch they refer -to. -@end table - -@ignore -@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -@c @node status examples -@appendixsubsec status examples - -@c -- FIXME -@end ignore - -@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node tag -@appendixsec tag---Add a symbolic tag to checked out version of RCS file +@appendixsec tag---Add a symbolic tag to checked out versions of files @c -- //////// - unnecessary. Also @c -- in a lot of other @c -- places. @@ -6356,7 +8622,7 @@ to. @itemize @bullet @item -tag [-lR] [-b] [-d] symbolic_tag [files@dots{}] +tag [-lR] [-b] [-c] [-d] symbolic_tag [files@dots{}] @item Requires: working directory, repository. @item @@ -6416,7 +8682,7 @@ different revision. This option is new in @sc{cvs} Local; run only in current working directory. @item -R -Commit directories recursively. This is on by default. +Tag directories recursively. This is on by default. @end table Two special options are available: @@ -6424,10 +8690,15 @@ Two special options are available: @table @code @item -b The -b option makes the tag a branch tag -(@pxref{Branches}), allowing concurrent, isolated +(@pxref{Revisions and branches}), allowing concurrent, isolated development. This is most useful for creating a patch to a previously released software distribution. +@item -c +The -c option checks that all files which are to be tagged are +unmodified. This can be used to make sure that you can reconstruct the +current file contents. + @item -d Delete a tag. @@ -6473,7 +8744,6 @@ since your last checkout or update. @menu * update options:: update options * update output:: update output -* update examples:: update examples @end menu @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -6501,20 +8771,21 @@ Process @sc{rcs} keywords according to @var{kflag}. See co(1). This option is sticky; future updates of this file in this working directory will use the same @var{kflag}. The @code{status} command can be viewed -to see the sticky options. @xref{status}. +to see the sticky options. See @ref{Invoking CVS}, for +more information on the @code{status} command. @item -l Local; run only in current working directory. @xref{Recursive behavior}. @item -P -Prune empty directories. +Prune empty directories. See @ref{Moving directories}. @item -p Pipe files to the standard output. @item -R -Operate recursively. This is on by default. -@xref{Recursive behavior}. +Update directories recursively (default). @xref{Recursive +behavior}. @item -r tag Retrieve revision @var{tag}. This option is sticky, @@ -6591,9 +8862,9 @@ date. An optional date is specified by adding a colon @node update output @appendixsubsec update output -@code{update} keeps you informed of its progress by -printing a line for each file, preceded by one -character indicating the status of the file: +@code{update} and @code{checkout} keep you informed of +its progress by printing a line for each file, preceded +by one character indicating the status of the file: @table @code @item U @var{file} @@ -6603,6 +8874,11 @@ the repository but not in your source, and for files that you haven't changed but are not the most recent versions available in the repository. +@item P @var{file} +Like @samp{U}, but the @sc{cvs} server sends a patch +instead of an entire file. These two things accomplish +the same thing. + @item A @var{file} The file has been added to your private copy of the sources, and will be added to the source repository @@ -6632,6 +8908,8 @@ before you ran @code{update}) will be made. The exact name of that file is printed while @code{update} runs. @item C @var{file} +@cindex .# files +@cindex __ files (VMS) A conflict was detected while trying to merge your changes to @var{file} with changes from the source repository. @var{file} (the copy in your working @@ -6640,11 +8918,21 @@ on the two revisions; an unmodified copy of your file is also in your working directory, with the name @file{.#@var{file}.@var{revision}} where @var{revision} is the @sc{rcs} revision that your modified file started -from. (Note that some systems automatically purge +from. Resolve the conflict as described in +@ref{Conflicts example} +@c "some systems" as in out-of-the-box OSes? Not as +@c far as I know. We need to advise sysadmins as well +@c as users how to set up this kind of purge, if that is +@c what they want. +@c We also might want to think about cleaner solutions, +@c like having CVS remove the .# file once the conflict +@c has been resolved or something like that. +(Note that some systems automatically purge files that begin with @file{.#} if they have not been accessed for a few days. If you intend to keep a copy of your original file, it is a very good idea to rename -it.) +it.) Under @sc{vms}, the file name starts with +@file{__} rather than @file{.#}. @item ? @var{file} @var{file} is in your working directory, but does not @@ -6652,28 +8940,712 @@ correspond to anything in the source repository, and is not in the list of files for @sc{cvs} to ignore (see the description of the @samp{-I} option, and @pxref{cvsignore}). +@end table -Note that no warning message like this is printed for -spurious directories that @sc{cvs} encounters. The -directory, and all its contents, are silently ignored. +@node Invoking CVS +@appendix Quick reference to CVS commands +@cindex Command reference +@cindex Reference, commands +@cindex Invoking CVS + +This appendix describes how to invoke @sc{cvs}, with +references to where each command or feature is +described in detail. Other relevant references are the +@samp{--help}/@samp{-H} option to @sc{cvs} +(@pxref{Global options}) and @ref{Index}. + +@c The idea behind this table is that we want each item +@c to be a sentence or two at most. Preferably a +@c single line. +@c +@c In some cases refs to "foo options" are just to get +@c this thing written quickly, not because the "foo +@c options" node is really the best place to point. +@table @code +@item add [@var{options}] [@var{files}@dots{}] +Add a new file/directory. See @ref{Adding files}. + +@table @code +@item -k @var{kflag} +Set keyword expansion. + +@item -m @var{msg} +Set file description. @end table -@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -@node update examples -@appendixsubsec update examples +@item admin [@var{options}] [@var{files}@dots{}] +Administration of history files in the repository. See +@ref{admin}. +@c This list omits those options which are not +@c documented as being useful with CVS. That might be +@c a mistake... + +@table @code +@item -b[@var{rev}] +Set default branch. +@c FIXME: Should xref to a section which describes how +@c to use this with the vendor branch. -The following line will display all files which are not -up-to-date without actually change anything in your -working directory. It can be used to check what has -been going on with the project. +@item -c@var{string} +Set comment leader. -@example -$ cvs -n -q update -@end example +@item -k@var{subst} +Set keyword substitution. See @ref{Keyword +substitution}. + +@item -l[@var{rev}] +Lock revision @var{rev}, or latest revision. + +@item -m@var{rev}:@var{msg} +Replace the log message of revision @var{rev} with +@var{msg}. + +@item -o@var{range} +Delete revisions from the history files + +@item -q +Run quietly; do not print diagnostics. + +@item -s@var{state}[:@var{rev}] +Set the state. + +@c Does not work for client/server CVS +@item -t +Set file description from standard input. + +@item -t@var{file} +Set file description from @var{file}. + +@item -t-@var{string} +Set file description to @var{string}. + +@item -u[@var{rev}] +Unlock revision @var{rev}, or latest revision. +@end table + +@item annotate [@var{options}] [@var{files}@dots{}] +Show last revision where each line was modified. See +@ref{annotate}. + +@table @code +@item -D @var{date} +Annotate the most recent revision no later than +@var{date}. See @ref{Common options}. + +@item -f +Use head revision if tag/date not found. See +@ref{Common options}. + +@item -l +Local; run only in current working directory. @xref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{tag} +Annotate revision @var{tag}. See @ref{Common options}. +@end table + +@item checkout [@var{options}] @var{modules}@dots{} +Get a copy of the sources. See @ref{checkout}. + +@table @code +@item -A +Reset any sticky tags/date/options. See @ref{Sticky +tags} and @ref{Keyword substitution}. + +@item -c +Output the module database. See @ref{checkout options}. + +@item -D @var{date} +Check out revisions as of @var{date} (is sticky). See +@ref{Common options}. + +@item -d @var{dir} +Check out into @var{dir}. See @ref{checkout options}. + +@item -f +Use head revision if tag/date not found. See +@ref{Common options}. + +@c Probably want to use rev1/rev2 style like for diff +@c -r. Here and in on-line help. +@item -j @var{rev} +Merge in changes. See @ref{checkout options}. + +@item -k @var{kflag} +Use @var{kflag} keyword expansion. See +@ref{Substitution modes}. + +@item -l +Local; run only in current working directory. @xref{Recursive behavior}. + +@item -N +Don't shorten module paths if -d specified. See @ref{checkout options}. + +@item -n +Do not run module program (if any). See @ref{checkout options}. + +@item -P +Prune empty directories. See @ref{Moving directories}. + +@item -p +Check out files to standard output (avoids +stickiness). See @ref{checkout options}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{tag} +Checkout revision @var{tag} (is sticky). See @ref{Common options}. + +@item -s +Like -c, but include module status. See @ref{checkout options}. +@end table + +@item commit [@var{options}] [@var{files}@dots{}] +Check changes into the repository. See @ref{commit}. + +@table @code +@item -F @var{file} +Read log message from @var{file}. See @ref{commit options}. + +@item -f +@c What is this "disables recursion"? It is from the +@c on-line help; is it documented in this manual? +Force the file to be committed; disables recursion. +See @ref{commit options}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -m @var{msg} +Use @var{msg} as log message. See @ref{commit options}. + +@item -n +Do not run module program (if any). See @ref{commit options}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{rev} +Commit to @var{rev}. See @ref{commit options}. +@c FIXME: should be dragging over text from +@c commit options, especially if it can be cleaned up +@c and made concise enough. +@end table + +@item diff [@var{options}] [@var{files}@dots{}] +Show differences between revisions. See @ref{diff}. +In addition to the options shown below, accepts a wide +variety of options to control output style, for example +@samp{-c} for context diffs. + +@table @code +@item -D @var{date1} +Diff revision for date against working file. See +@ref{diff options}. + +@item -D @var{date2} +Diff @var{rev1}/@var{date1} against @var{date2}. See +@ref{diff options}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -N +Include diffs for added and removed files. See +@ref{diff options}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{rev1} +Diff revision for @var{rev1} against working file. See +@ref{diff options}. + +@item -r @var{rev2} +Diff rev1/date1 against rev2. See @ref{diff options}. +@end table + +@item edit [@var{options}] [@var{files}@dots{}] +Get ready to edit a watched file. See @ref{Editing files}. + +@table @code +@item -a @var{actions} +Specify actions for temporary watch, where +@var{actions} is @code{edit}, @code{unedit}, +@code{commit}, @code{all}, or @code{none}. See +@ref{Editing files}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. +@end table + +@item editors [@var{options}] [@var{files}@dots{}] +See who is editing a watched file. See @ref{Watch information}. + +@table @code +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. +@end table + +@item export [@var{options}] @var{modules}@dots{} +Export files from CVS. See @ref{export}. + +@table @code +@item -D @var{date} +Check out revisions as of @var{date}. See +@ref{Common options}. + +@item -d @var{dir} +Check out into @var{dir}. See @ref{export options}. + +@item -f +Use head revision if tag/date not found. See +@ref{Common options}. + +@item -k @var{kflag} +Use @var{kflag} keyword expansion. See +@ref{Substitution modes}. + +@item -l +Local; run only in current working directory. @xref{Recursive behavior}. + +@item -N +Don't shorten module paths if -d specified. See @ref{export options}. + +@item -n +Do not run module program (if any). See @ref{export options}. + +@item -P +Prune empty directories. See @ref{Moving directories}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{tag} +Checkout revision @var{tag} (is sticky). See @ref{Common options}. +@end table + +@item history [@var{options}] [@var{files}@dots{}] +Show repository access history. See @ref{history}. + +@table @code +@item -a +All users (default is self). See @ref{history options}. + +@item -b @var{str} +Back to record with @var{str} in module/file/repos +field. See @ref{history options}. + +@item -c +Report on committed (modified) files. See @ref{history options}. + +@item -D @var{date} +Since @var{date}. See @ref{history options}. + +@item -e +Report on all record types. See @ref{history options}. + +@item -l +Last modified (committed or modified report). See @ref{history options}. + +@item -m @var{module} +Report on @var{module} (repeatable). See @ref{history +options}. + +@item -n @var{module} +In @var{module}. See @ref{history options}. + +@item -o +Report on checked out modules. See @ref{history options}. + +@item -r @var{rev} +Since revision @var{rev}. See @ref{history options}. + +@item -T +@c What the @#$@# is a TAG? Same as a tag? This +@c wording is also in the online-line help. +Produce report on all TAGs. See @ref{history options}. + +@item -t @var{tag} +Since tag record placed in history file (by anyone). +See @ref{history options}. + +@item -u @var{user} +For user @var{user} (repeatable). See @ref{history +options}. + +@item -w +Working directory must match. See @ref{history options}. + +@item -x @var{types} +Report on @var{types}, one or more of +@code{TOEFWUCGMAR}. See @ref{history options}. + +@item -z @var{zone} +Output for time zone @var{zone}. See @ref{history +options}. +@end table + +@item import [@var{options}] @var{repository} @var{vendor-tag} @var{release-tags}@dots{} +Import files into CVS, using vendor branches. See +@ref{import}. + +@table @code +@item -b @var{bra} +Import to vendor branch @var{bra}. See +@ref{import options}. + +@item -d +Use the file's modification time as the time of +import. See @ref{import options}. + +@item -k @var{kflag} +Set default RCS keyword substitution mode. See +@ref{import options}. + +@item -m @var{msg} +Use @var{msg} for log message. See +@ref{import options}. + +@item -I @var{ign} +More files to ignore (! to reset). See +@ref{import options}. + +@item -W @var{spec} +More wrappers. See @ref{import options}. +@end table + +@item init +Create a CVS repository if it doesn't exist. See +@ref{Creating a repository}. + +@item log [@var{options}] [@var{files}@dots{}] +Print out history information for files. See @ref{log}. + +@table @code +@item -b +Only list revisions on the default branch. See @ref{log options}. + +@item -d @var{dates} +Specify dates (@var{d1}<@var{d2} for range, @var{d} for +latest before). See @ref{log options}. + +@item -h +Only print header. See @ref{log options}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -N +Do not list tags. See @ref{log options}. + +@item -R +Only print name of RCS file. See @ref{log options}. + +@item -r @var{revs} +Only list revisions @var{revs}. See @ref{log options}. + +@item -s @var{states} +Only list revisions with specified states. See @ref{log options}. + +@item -t +Only print header and descriptive text. See @ref{log +options}. + +@item -w @var{logins} +Only list revisions checked in by specified logins. See @ref{log options}. +@end table + +@item login +Prompt for password for authenticating server. See +@ref{Password authentication client}. + +@item logout +Remove stored password for authenticating server. See +@ref{Password authentication client}. + +@item rdiff [@var{options}] @var{modules}@dots{} +Show differences between releases. See @ref{rdiff}. + +@table @code +@item -c +Context diff output format (default). See @ref{rdiff options}. + +@item -D @var{date} +Select revisions based on @var{date}. See @ref{Common options}. + +@item -f +Use head revision if tag/date not found. See +@ref{Common options}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{rev} +Select revisions based on @var{rev}. See @ref{Common options}. + +@item -s +Short patch - one liner per file. See @ref{rdiff options}. + +@item -t +Top two diffs - last change made to the file. See +@ref{diff options}. + +@item -u +Unidiff output format. See @ref{rdiff options}. + +@item -V @var{vers} +Use RCS Version @var{vers} for keyword expansion. See +@ref{rdiff options}. +@end table + +@item release [@var{options}] @var{directory} +Indicate that a directory is no longer in use. See +@ref{release}. + +@table @code +@item -d +Delete the given directory. See @ref{release options}. +@end table + +@item remove [@var{options}] [@var{files}@dots{}] +Remove an entry from the repository. See @ref{Removing files}. + +@table @code +@item -f +Delete the file before removing it. See @ref{Removing files}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. +@end table + +@item rtag [@var{options}] @var{tag} @var{modules}@dots{} +Add a symbolic tag to a module. See @ref{rtag}. + +@table @code +@c Is this one of those dumb options which used to +@c work around the lack of death support? +@item -a +Clear tag from removed files that would not otherwise +be tagged. See @ref{rtag options}. + +@item -b +Create a branch named @var{tag}. See @ref{rtag options}. + +@item -D @var{date} +Tag revisions as of @var{date}. See @ref{rtag options}. + +@item -d +Delete the given tag. See @ref{rtag options}. + +@item -F +Move tag if it already exists. See @ref{rtag options}. + +@item -f +Force a head revision match if tag/date not found. +See @ref{rtag options}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -n +No execution of tag program. See @ref{rtag options}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{tag} +Tag existing tag @var{tag}. See @ref{rtag options}. +@end table + +@item status [@var{options}] @var{files}@dots{} +Display status information in a working directory. See +@ref{File status}. + +@table @code +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -v +Include tag information for file. See @ref{Tags}. +@end table + +@item tag [@var{options}] @var{tag} [@var{files}@dots{}] +Add a symbolic tag to checked out version of files. +See @ref{tag}. + +@table @code +@item -b +Create a branch named @var{tag}. See @ref{tag options}. + +@item -D @var{date} +Tag revisions as of @var{date}. See @ref{tag options}. + +@item -d +Delete the given tag. See @ref{tag options}. + +@item -F +Move tag if it already exists. See @ref{tag options}. + +@item -f +Force a head revision match if tag/date not found. +See @ref{tag options}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -n +No execution of tag program. See @ref{tag options}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{tag} +Tag existing tag @var{tag}. See @ref{tag options}. +@end table + +@item unedit [@var{options}] [@var{files}@dots{}] +Undo an edit command. See @ref{Editing files}. + +@table @code +@item -a @var{actions} +Specify actions for temporary watch, where +@var{actions} is @code{edit}, @code{unedit}, +@code{commit}, @code{all}, or @code{none}. See +@ref{Editing files}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. +@end table + +@item update [@var{options}] [@var{files}@dots{}] +Bring work tree in sync with repository. See +@ref{update}. + +@table @code +@item -A +Reset any sticky tags/date/options. See @ref{Sticky +tags} and @ref{Keyword substitution}. + +@item -D @var{date} +Check out revisions as of @var{date} (is sticky). See +@ref{Common options}. + +@item -d +Create directories. See @ref{update options}. + +@item -f +Use head revision if tag/date not found. See +@ref{Common options}. + +@item -I @var{ign} +More files to ignore (! to reset). See +@ref{import options}. + +@c Probably want to use rev1/rev2 style like for diff +@c -r. Here and in on-line help. +@item -j @var{rev} +Merge in changes. See @ref{update options}. + +@item -k @var{kflag} +Use @var{kflag} keyword expansion. See +@ref{Substitution modes}. + +@item -l +Local; run only in current working directory. @xref{Recursive behavior}. + +@item -P +Prune empty directories. See @ref{Moving directories}. + +@item -p +Check out files to standard output (avoids +stickiness). See @ref{update options}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{tag} +Checkout revision @var{tag} (is sticky). See @ref{Common options}. + +@item -W @var{spec} +More wrappers. See @ref{import options}. +@end table + +@item watch [on|off|add|remove] [@var{options}] [@var{files}@dots{}] + +on/off: turn on/off read-only checkouts of files. See +@ref{Setting a watch}. + +add/remove: add or remove notification on actions. See +@ref{Getting Notified}. + +@table @code +@item -a @var{actions} +Specify actions for temporary watch, where +@var{actions} is @code{edit}, @code{unedit}, +@code{commit}, @code{all}, or @code{none}. See +@ref{Editing files}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. +@end table + +@item watchers [@var{options}] [@var{files}@dots{}] +See who is watching a file. See @ref{Watch information}. + +@table @code +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. +@end table + +@end table @c --------------------------------------------------------------------- @node Administrative files -@appendix Reference manual for the Administrative files +@appendix Reference manual for Administrative files @cindex Administrative files (reference) @cindex Files, reference manual @cindex Reference manual (files) @@ -6683,7 +9655,9 @@ Inside the repository, in the directory @file{$CVSROOT/CVSROOT}, there are a number of supportive files for @sc{cvs}. You can use @sc{cvs} in a limited fashion without any of them, but if they are set up -properly they can help make life easier. +properly they can help make life easier. For a +discussion of how to edit them, @xref{Intro +administrative files}. The most important of these files is the @file{modules} file, which defines the modules inside the repository. @@ -6693,12 +9667,13 @@ file, which defines the modules inside the repository. * Wrappers:: Treat directories as files * commit files:: The commit support files * commitinfo:: Pre-commit checking -* editinfo:: Specifying how log messages are created +* verifymsg:: How are log messages evaluated? +* editinfo:: Specifying how log messages are created + (obsolete) * loginfo:: Where should log messages be sent? * rcsinfo:: Templates for the log messages * cvsignore:: Ignoring files via cvsignore * history file:: History information -* Setting up:: Setting up the repository * Variables:: Various variables are expanded @end menu @@ -6771,9 +9746,33 @@ in the @sc{cvs} source repository. A module definition can refer to other modules by including @samp{&@var{module}} in its definition. @code{checkout} creates a subdirectory for each such -module, in your working directory. -@c -- Nope. "in your working directory" is wrong. What -@c -- is right? +module, in the directory containing the module. For +example, if modules contains + +@example +m4test &unsupported +@end example + +then a checkout will create an @code{m4test} directory +which contains a directory called @code{unsupported}, +which in turns contains all the directories and files +which live there. +@c FIXME: this is hard to describe since we don't tell +@c the user what the repository contains. Best way to +@c fix this whole mess is an extended example where we +@c first say what is in the repository, then show a +@c regular module, an alias module, and an & module. +@c We should mention the concept of options only +@c *after* we've taken care of those basics. +@c +@c FIXCVS: What happens if regular and & modules are +@c combined, as in "ampermodule first-dir &second-dir"? +@c When I tried it, it seemed to just ignore the +@c "first-dir". I think perhaps it should be an error +@c (but this needs further investigation). +@c In addition to discussing what each one does, we +@c should put in a few words about why you would use one or +@c the other in various situations. @table @code @item -d @var{name} @@ -6785,6 +9784,7 @@ module name. Specify a program @var{prog} to run whenever files in a module are exported. @var{prog} runs with a single argument, the module name. +@c FIXME: Is it run on server? client? @cindex Checkin program @item -i @var{prog} @@ -6792,14 +9792,16 @@ Specify a program @var{prog} to run whenever files in a module are committed. @var{prog} runs with a single argument, the full pathname of the affected directory in a source repository. The @file{commitinfo}, -@file{loginfo}, and @file{editinfo} files provide other +@file{loginfo}, and @file{verifymsg} files provide other ways to call a program on commit. +@c FIXME: Is it run on server? client? @cindex Checkout program @item -o @var{prog} Specify a program @var{prog} to run whenever files in a module are checked out. @var{prog} runs with a single argument, the module name. +@c FIXME: Is it run on server? client? @cindex Status of a module @cindex Module status @@ -6819,6 +9821,7 @@ module are tagged with @code{rtag}. @var{prog} runs with two arguments: the module name and the symbolic tag specified to @code{rtag}. There is no way to specify a program to run when @code{tag} is executed. +@c FIXME: Is it run on server? client? @cindex Update program @item -u @var{prog} @@ -6827,6 +9830,7 @@ update} is executed from the top-level directory of the checked-out module. @var{prog} runs with a single argument, the full path to the source repository for this module. +@c FIXME: Is it run on server? client? @end table @end table @@ -6837,9 +9841,14 @@ this module. @cindex CVSWRAPPERS, environment variable @cindex Wrappers +@c FIXME: need some better way of separating this out +@c by functionality. -t/-f is one feature, -m is +@c another, and -k is a third. And this discussion +@c should be better motivated (e.g. start with the +@c problems, then explain how the feature solves it). + Wrappers allow you to set a hook which transforms files on -their way in and out of @sc{cvs}. Most or all of the -wrappers features do not work with client/server @sc{cvs}. +their way in and out of @sc{cvs}. The file @file{cvswrappers} defines the script that will be run on a file when its name matches a regular @@ -6848,7 +9857,8 @@ file or directory. One script is executed on the file/directory before being checked into the repository (this is denoted with the @code{-t} flag) and the other when the file is checked out of the repository (this is denoted with the -@code{-f} flag) +@code{-f} flag). The @samp{-t}/@samp{-f} feature does +not work with client/server @sc{cvs}. The @file{cvswrappers} also has a @samp{-m} option to specify the merge methodology that should be used when @@ -6867,13 +9877,16 @@ binary files. The basic format of the file @file{cvswrappers} is: +@c FIXME: @example is all wrong for this. Use @deffn or +@c something more sensible. @example wildcard [option value][option value]... where option is one of --f from cvs filter value: path tofilter +-f from cvs filter value: path to filter -t to cvs filter value: path to filter -m update methodology value: MERGE or COPY +-k keyword expansion value: expansion mode and value is a single-quote delimited value. @end example @@ -6894,6 +9907,11 @@ file is checked out of the repository. The methodology should be used when updating the files in the repository (that is no merging should be performed). +@c What pitfalls arise when using indent this way? Is +@c it a winning thing to do? Would be nice to at least +@c hint at those issues; we want our examples to tell +@c how to solve problems, not just to say that cvs can +@c do certain things. The last example line says that all files that end with a @code{*.c} should be filtered with @file{indent} before being checked into the repository. Unlike the previous @@ -6911,6 +9929,57 @@ which is the name of the file to filter from. The end result of this filter will be a file in the users directory that they can work on as they normally would. +Note that the @samp{-t}/@samp{-f} features do not +conveniently handle one portion of CVS's operation: +determining when files are modified. CVS will still +want a file (or directory) to exist, and it will use +its modification time to determine whether a file is +modified. If CVS erroneously thinks a file is +unmodified (for example, a directory is unchanged but +one of the files within it is changed), you can force +it to check in the file anyway by specifying the +@samp{-f} option to @code{cvs commit} (@pxref{commit +options}). +@c This is, of course, a serious design flaw in -t/-f. +@c Probably the whole functionality needs to be +@c redesigned (starting from requirements) to fix this. + +For another example, the following command imports a +directory, treating files whose name ends in +@samp{.exe} as binary: + +@example +cvs import -I ! -W "*.exe -k 'b'" first-dir vendortag reltag +@end example + +@c Another good example, would be storing files +@c (e.g. binary files) compressed in the repository. +@c :::::::::::::::::: +@c cvswrappers +@c :::::::::::::::::: +@c *.t12 -m 'COPY' +@c *.t[0-9][0-9] -f 'gunzipcp %s' -t 'gzipcp %s %s' -m 'COPY' +@c +@c :::::::::::::::::: +@c gunzipcp +@c :::::::::::::::::: +@c : +@c [ -f $1 ] || exit 1 +@c zcat $1 > /tmp/.#$1.$$ +@c mv /tmp/.#$1.$$ $1 +@c +@c :::::::::::::::::: +@c gzipcp +@c :::::::::::::::::: +@c : +@c DIRNAME=`echo $1 | sed -e "s|/.*/||g"` +@c if [ ! -d $DIRNAME ] ; then +@c DIRNAME=`echo $1 | sed -e "s|.*/||g"` +@c fi +@c gzip -c $DIRNAME > $2 +@c One catch--"cvs diff" will not invoke the wrappers +@c (probably a CVS bug, although I haven't thought it out). + @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node commit files @appendixsec The commit support files @@ -6934,12 +10003,19 @@ The program is responsible for checking that the commit is allowed. If it exits with a non-zero exit status the commit will be aborted. +@item verifymsg +The specified program is used to evaluate the log message, +and possibly verify that it contains all required +fields. This is most useful in combination with the +@file{rcsinfo} file, which can hold a log message +template (@pxref{rcsinfo}). + @item editinfo The specified program is used to edit the log message, and possibly verify that it contains all required fields. This is most useful in combination with the @file{rcsinfo} file, which can hold a log message -template (@pxref{rcsinfo}). +template (@pxref{rcsinfo}). (obsolete) @item loginfo The specified program is called when the commit is @@ -6961,15 +10037,34 @@ imagination is the limit! @cindex Syntax of info files @cindex Common syntax of info files -The four files @file{commitinfo}, @file{loginfo}, -@file{rcsinfo} and @file{editinfo} all have a common -format. The purpose of the files are described later -on. The common syntax is described here. +@c FIXME: having this so totally separate from the +@c Variables node is rather bogus. + +The administrative files such as @file{commitinfo}, +@file{loginfo}, @file{rcsinfo}, @file{verifymsg}, etc., +all have a common format. The purpose of the files are +described later on. The common syntax is described +here. +@cindex regular expression syntax Each line contains the following: @itemize @bullet @item -A regular expression +@c Say anything about DEFAULT and ALL? Right now we +@c leave that to the description of each file (and in fact +@c the practice is inconsistent which is really annoying). +A regular expression. This is a basic regular +expression in the syntax used by GNU emacs. +@c FIXME: What we probably should be saying is "POSIX Basic +@c Regular Expression with the following extensions (`\(' +@c `\|' '+' etc)" +@c rather than define it with reference to emacs. +@c The reference to emacs is not strictly speaking +@c true, as we don't support \=, \s, or \S. Also it isn't +@c clear we should document and/or promise to continue to +@c support all the obscure emacs extensions like \<. +@c Also need to better cite (or include) full +@c documentation for the syntax. @item A whitespace separator---one or more spaces and/or tabs. @@ -6988,6 +10083,13 @@ The first regular expression that matches the current directory name in the repository is used. The rest of the line is used as a file name or command-line as appropriate. +@c FIXME: need an example. In particular, show what +@c the regular expression is matched against (one +@c ordinarily clueful person got confused about whether it +@c includes the filename--"directory name" above should be +@c unambiguous but there is nothing like an example to +@c confirm people's understanding of this sort of thing). + @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node commitinfo @appendixsec Commitinfo @@ -7032,14 +10134,129 @@ Note: when @sc{CVS} is accessing a remote repository, (i.e., server) side, not the client side (@pxref{Remote repositories}). +@c FIXME: should discuss using commitinfo to control +@c who has checkin access to what (e.g. Joe can check into +@c directories a, b, and c, and Mary can check into +@c directories b, c, and d--note this case cannot be +@c conveniently handled with unix groups). Of course, +@c adding a new set of features to CVS might be a more +@c natural way to fix this problem than telling people to +@c use commitinfo. +@c FIXME: Should make some reference, especially in +@c the context of controlling who has access, to the fact +@c that commitinfo can be circumvented. Perhaps +@c mention SETXID (but has it been carefully examined +@c for holes?). This fits in with the discussion of +@c general CVS security in "Password authentication +@c security" (the bit which is not pserver-specific). + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node verifymsg +@appendixsec Verifying log messages +@cindex verifymsg (admin file) +@cindex log message, verifying + +Once you have entered a log message, you can evaluate +that message to check for specific content, such as +a bug ID. Use the @file{verifymsg} file to +specify a program that is used to verify the log message. +This program could be a simple script that checks +that the entered message contains the required fields. + +The @file{verifymsg} file is often most useful together +with the @file{rcsinfo} file, which can be used to +specify a log message template. + +Each line in the @file{verifymsg} file consists of a +regular expression and a command-line template. The +template must include a program name, and can include +any number of arguments. The full path to the current +log message template file is appended to the template. + +One thing that should be noted is that the @samp{ALL} +keyword is not supported. If more than one matching +line is found, the first one is used. This can be +useful for specifying a default verification script in a +module, and then overriding it in a subdirectory. + +@cindex DEFAULT in verifymsg +If the repository name does not match any of the +regular expressions in this file, the @samp{DEFAULT} +line is used, if it is specified. + +If the verification script exits with a non-zero exit status, +the commit is aborted. + +Note that the verification script cannot change the log +message; it can merely accept it or reject it. +@c FIXME? Is this an annoying limitation? It would be +@c relatively easy to fix (although it would *not* be a +@c good idea for a verifymsg script to interact with the user +@c at least in the client/server case because of locks +@c and all that jazz). + +The following is a little silly example of a +@file{verifymsg} file, together with the corresponding +@file{rcsinfo} file, the log message template and an +verification script. We begin with the log message template. +We want to always record a bug-id number on the first +line of the log message. The rest of log message is +free text. The following template is found in the file +@file{/usr/cvssupport/tc.template}. + +@example +BugId: +@end example + +The script @file{/usr/cvssupport/bugid.verify} is used to +evaluate the log message. + +@example +#!/bin/sh +# +# bugid.verify filename +# +# Verify that the log message contains a valid bugid +# on the first line. +# +if head -1 < $1 | grep '^BugId:[ ]*[0-9][0-9]*$' > /dev/null; then + exit 0 +else + echo "No BugId found." + exit 1 +fi +@end example + +The @file{verifymsg} file contains this line: + +@example +^tc /usr/cvssupport/bugid.edit +@end example + +The @file{rcsinfo} file contains this line: + +@example +^tc /usr/cvssupport/tc.template +@end example + + + @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node editinfo @appendixsec Editinfo -@cindex Editinfo +@cindex editinfo (admin file) @cindex Editor, specifying per module @cindex Per-module editor @cindex Log messages, editing +@emph{NOTE:} The @file{editinfo} feature has been +rendered obsolete. To set a default editor for log +messages use the @code{EDITOR} environment variable +(@pxref{Environment variables}) or the @samp{-e} global +option (@pxref{Global options}). See @ref{verifymsg}, +for information on the use of the @file{verifymsg} +feature for evaluating log messages. + If you want to make sure that all log messages look the same way, you can use the @file{editinfo} file to specify a program that is used to edit the log message. @@ -7053,8 +10270,7 @@ file, the editor specified in the environment variable @code{$CVSEDITOR} is used instead. If that variable is not set, then the environment variable @code{$EDITOR} is used instead. If that variable is not -set a precompiled default, normally @code{vi}, will be -used. +set a default will be used. See @ref{Committing your changes}. The @file{editinfo} file is often most useful together with the @file{rcsinfo} file, which can be used to @@ -7081,9 +10297,10 @@ If the edit script exits with a non-zero exit status, the commit is aborted. Note: when @sc{CVS} is accessing a remote repository, -@file{editinfo} will be run on the @emph{remote} -(i.e., server) side, not the client side (@pxref{Remote -repositories}). +or when the @samp{-m} or @samp{-F} options to @code{cvs +commit} are used, @file{editinfo} will not be consulted. +There is no good workaround for this; use +@file{verifymsg} instead. @menu * editinfo example:: Editinfo example @@ -7145,7 +10362,7 @@ The @file{rcsinfo} file contains this line: @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node loginfo @appendixsec Loginfo -@cindex Loginfo +@cindex loginfo (admin file) @cindex Storing log messages @cindex Mailing log messages @cindex Distributing log messages @@ -7159,11 +10376,6 @@ relative to the @code{$CVSROOT}. If a match is found, then the remainder of the line is a filter program that should expect log information on its standard input. -The filter program may use one and only one % modifier -(a la printf). If @samp{%s} is specified in the filter -program, a brief title is included (enclosed in single -quotes) showing the modified file names. - If the repository name does not match any of the regular expressions in this file, the @samp{DEFAULT} line is used, if it is specified. @@ -7177,6 +10389,46 @@ The first matching regular expression is used. @xref{commit files}, for a description of the syntax of the @file{loginfo} file. +The user may specify a format string as +part of the filter. The string is composed of a +@samp{%} followed by a space, or followed by a single +format character, or followed by a set of format +characters surrounded by @samp{@{} and @samp{@}} as +separators. The format characters are: + +@table @t +@item s +file name +@item V +old version number (pre-checkin) +@item v +new version number (post-checkin) +@end table + +All other characters that appear in a format string +expand to an empty field (commas separating fields are +still provided). + +For example, some valid format strings are @samp{%}, +@samp{%s}, @samp{%@{s@}}, and @samp{%@{sVv@}}. + +The output will be a string of tokens separated by +spaces. For backwards compatibility, the the first +token will be the repository name. The rest of the +tokens will be comma-delimited lists of the information +requested in the format string. For example, if +@samp{/u/src/master} is the repository, @samp{%@{sVv@}} +is the format string, and three files (@t{ChangeLog}, +@t{Makefile}, @t{foo.c}) were modified, the output +might be: + +@example +/u/src/master ChangeLog,1.1,1.2 Makefile,1.3,1.4 foo.c,1.12,1.13 +@end example + +As another example, @samp{%@{@}} means that only the +name of the repository will be generated. + Note: when @sc{CVS} is accessing a remote repository, @file{loginfo} will be run on the @emph{remote} (i.e., server) side, not the client side (@pxref{Remote @@ -7184,6 +10436,7 @@ repositories}). @menu * loginfo example:: Loginfo example +* Keeping a checked out copy:: Updating a tree on every checkin @end menu @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -7214,17 +10467,66 @@ like this: @example #!/bin/sh -(echo "-----------------------------------------------------------------"; +(echo "------------------------------------------------------"; echo -n $USER" "; date; echo; sed '1s+'$@{CVSROOT@}'++') >> $1 @end example +@node Keeping a checked out copy +@appendixsubsec Keeping a checked out copy + +@c What other index entries? It seems like +@c people might want to use a lot of different +@c words for this functionality. +@cindex keeping a checked out copy +@cindex checked out copy, keeping +@cindex web pages, maintaining with CVS + +It is often useful to maintain a directory tree which +contains files which correspond to the latest version +in the repository. For example, other developers might +want to refer to the latest sources without having to +check them out, or you might be maintaining a web site +with @sc{cvs} and want every checkin to cause the files +used by the web server to be updated. +@c Can we offer more details on the web example? Or +@c point the user at how to figure it out? This text +@c strikes me as sufficient for someone who already has +@c some idea of what we mean but not enough for the naive +@c user/sysadmin to understand it and set it up. + +The way to do this is by having loginfo invoke +@code{cvs update}. Doing so in the naive way will +cause a problem with locks, so the @code{cvs update} +must be run in the background. +@c Should we try to describe the problem with locks? +@c It seems like a digression for someone who just +@c wants to know how to make it work. +@c Another choice which might work for a single file +@c is to use "cvs -n update -p" which doesn't take +@c out locks (I think) but I don't see many advantages +@c of that and we might as well document something which +@c works for multiple files. +Here is an example (this should all be on one line): + +@example +^cyclic-pages (date; cat; (sleep 2; cd /u/www/local-docs; + cvs -q update -d) &) >> $CVSROOT/CVSROOT/updatelog 2>&1 +@end example + +This will cause checkins to repository directories +starting with @code{cyclic-pages} to update the checked +out tree in @file{/u/www/local-docs}. +@c More info on some of the details? The "sleep 2" is +@c so if we are lucky the lock will be gone by the time +@c we start and we can wait 2 seconds instead of 30. + @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node rcsinfo @appendixsec Rcsinfo -@cindex Rcsinfo +@cindex rcsinfo (admin file) @cindex Form for log message @cindex Log message template @cindex Template for log message @@ -7232,7 +10534,7 @@ like this: The @file{rcsinfo} file can be used to specify a form to edit when filling out the commit log. The @file{rcsinfo} file has a syntax similar to the -@file{editinfo}, @file{commitinfo} and @file{loginfo} +@file{verifymsg}, @file{commitinfo} and @file{loginfo} files. @xref{syntax}. Unlike the other files the second part is @emph{not} a command-line template. Instead, the part after the regular expression should be a full pathname to @@ -7246,13 +10548,26 @@ All occurances of the name @samp{ALL} appearing as a regular expression are used in addition to the first matching regular expression or @samp{DEFAULT}. +@c FIXME: should be offering advice, somewhere around +@c here, about where to put the template file. The +@c verifymsg example uses /usr/cvssupport but doesn't +@c say anything about what that directory is for or +@c whether it is hardwired into CVS or who creates +@c it or anything. In particular we should say +@c how to version control the template file. A +@c probably better answer than the /usr/cvsssupport +@c stuff is to use checkoutlist. FIXME: it doesn't +@c seem like checkoutlist is documented at all! +@c Also I am starting to see a connection between +@c this and the Keeping a checked out copy node. +@c Probably want to say something about that. The log message template will be used as a default log message. If you specify a log message with @samp{cvs commit -m @var{message}} or @samp{cvs commit -f @var{file}} that log message will override the template. -@xref{editinfo example}, for an example @file{rcsinfo} +@xref{verifymsg}, for an example @file{rcsinfo} file. When @sc{CVS} is accessing a remote repository, @@ -7269,7 +10584,7 @@ directory. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node cvsignore @appendixsec Ignoring files via cvsignore -@cindex Cvsignore, global +@cindex cvsignore (admin file), global @cindex Global cvsignore @cindex Ignoring files @c -- This chapter should maybe be moved to the @@ -7343,6 +10658,20 @@ exclamation mark (@samp{!}) clears the ignore list. This can be used if you want to store any file which normally is ignored by @sc{cvs}. +Specifying @samp{-I !} to @code{cvs import} will import +everything, which is generally what you want to do if +you are importing files from a pristine distribution or +any other source which is known to not contain any +extraneous files. However, looking at the rules above +you will see there is a fly in the ointment; if the +distribution contains any @file{.cvsignore} files, then +the patterns from those files will be processed even if +@samp{-I !} is specified. The only workaround is to +remove the @file{.cvsignore} files in order to do the +import. Because this is awkward, in the future +@samp{-I !} might be modified to override +@file{.cvsignore} files in each directory. + @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node history file @appendixsec The history file @@ -7354,7 +10683,7 @@ to log information for the @code{history} command (@pxref{history}). This file must be created to turn on logging. This is done automatically if the @code{cvs init} command is used to set up the -repository (@pxref{Setting up}). +repository (@pxref{Creating a repository}). The file format of the @file{history} file is documented only in comments in the @sc{cvs} source @@ -7362,39 +10691,6 @@ code, but generally programs should use the @code{cvs history} command to access it anyway, in case the format changes with future releases of @sc{cvs}. -@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@node Setting up -@appendixsec Setting up the repository -@cindex Repository, setting up -@cindex Creating a repository -@cindex Setting up a repository - -To set up a @sc{cvs} repository, choose a directory -with ample disk space available for the revision -history of the source files. It should be accessable -(directly or via a networked file system) from all -machines which want to use @sc{cvs} in server or local -mode; the client machines need not have any access to -it other than via the @sc{cvs} protocol. - -To create a repository, run the @code{cvs init} -command. It will set up an empty repository in the -@sc{cvs} root specified in the usual way -(@pxref{Repository}). For example, - -@example -cvs -d /usr/local/cvsroot init -@end example - -@code{cvs init} is careful to never overwrite any -existing files in the repository, so no harm is done if -you run @code{cvs init} on an already set-up -repository. - -@code{cvs init} will enable history logging; if you -don't want that, remove the history file after running -@code{cvs init}. @xref{history file}. - @node Variables @appendixsec Expansions in administrative files @@ -7462,7 +10758,14 @@ particularly useful to specify this option via For example, if you want the administrative file to refer to a test directory you might create a user variable @code{TESTDIR}. Then if @sc{cvs} is invoked -as @code{cvs -s TESTDIR=/work/local/tests}, and the +as + +@example +cvs -s TESTDIR=/work/local/tests +@end example + +@noindent +and the administrative file contains @code{sh $@{=TESTDIR@}/runtests}, then that string is expanded to @code{sh /work/local/tests/runtests}. @@ -7492,6 +10795,7 @@ A whitespace-separated list of file name patterns that @sc{cvs} should treat as wrappers. @xref{Wrappers}. @cindex CVSREAD +@cindex read-only files, and CVSREAD @item $CVSREAD If this is set, @code{checkout} and @code{update} will try hard to make the files in your working directory @@ -7517,11 +10821,8 @@ directory. @item $EDITOR @itemx $CVSEDITOR Specifies the program to use for recording log messages -during commit. If not set, the default is -@samp{/usr/ucb/vi}. @code{$CVSEDITOR} overrides -@code{$EDITOR}. @code{$CVSEDITOR} does not exist in -@sc{cvs} 1.3, but the next release will probably -include it. +during commit. @code{$CVSEDITOR} overrides +@code{$EDITOR}. See @ref{Committing your changes}. @cindex PATH @item $PATH @@ -7531,9 +10832,10 @@ programs it uses. @cindex RCSBIN @item $RCSBIN -Specifies the full pathname of the location of @sc{rcs} programs, -such as co(1) and ci(1). If not set, a compiled-in -value is used, or your @code{$PATH} is searched. +This is the value @sc{cvs} is using for where to find +@sc{rcs} binaries. @xref{Global options}, for a +description of how to specify this. If not set, a +compiled-in value is used, or your @code{$PATH} is searched. @cindex HOME @item $HOME @@ -7545,13 +10847,9 @@ file is searched (@code{$HOMEPATH} is used for Windows-NT). @cindex CVS_RSH @item $CVS_RSH -Used in client-server mode when accessing a remote -repository using @sc{rsh}. The default value is -@code{rsh}. You can set it to use another program for -accssing the remote server (e.g. for HP-UX 9, you -should set it to @code{remsh} because @code{rsh} -invokes the restricted shell). @pxref{Connecting via -rsh} +Specifies the external program which CVS connects with, +when @code{:ext:} access method is specified. +@pxref{Connecting via rsh}. @item $CVS_SERVER Used in client-server mode when accessing a remote @@ -7565,11 +10863,6 @@ Used in client-server mode when accessing the @code{cvs login server}. Default value is @file{$HOME/.cvspass}. @pxref{Password authentication client} -@item $CVS_PASSWORD -Used in client-server mode when accessing the @code{cvs -login server}. -@pxref{Password authentication client} - @item $CVS_CLIENT_PORT Used in client-server mode when accessing the server via Kerberos. @@ -7605,10 +10898,37 @@ seconds so that you can attach to it with a debugger. Used under OS/2 only. It specifies the name of the command interpreter and defaults to @sc{cmd.exe}. +@cindex TMPDIR +@item $TMPDIR +@cindex TMP +@itemx $TMP +@cindex TEMP +@itemx $TEMP +@cindex temporary files, location of +@c I'm not even sure I've documented all the +@c conventions here. Furthermore, those conventions are +@c pretty crazy and they should be simplified. +Directory in which temporary files are located. Those +parts of @sc{cvs} which are implemented using @sc{rcs} +inspect the above variables in the order they appear +above and the first value found is taken; if none of +them are set, a host-dependent default is used, +typically @file{/tmp}. The @sc{cvs} server uses +@code{TMPDIR}. @xref{Global options}, for a +description of how to specify this. +Some parts of @sc{cvs} will always use @file{/tmp} (via +the @code{tmpnam} function provided by the system). + +On Windows NT, @code{TMP} is used (via the @code{_tempnam} +function provided by the system). +The @code{patch} program which is used by the @sc{cvs} +client uses @code{TMPDIR}, and if it is not set, uses +@file{/tmp} (at least with GNU patch 2.1). @end table -@sc{cvs} is a front-end to @sc{rcs}. The following environment +@sc{cvs} invokes @sc{rcs} to perform certain +operations. The following environment variables affect @sc{rcs}. Note that if you are using the client/server @sc{cvs}, these variables need to be set on the server side (which may or not may be @@ -7631,18 +10951,6 @@ Options prepended to the argument list, separated by spaces. A backslash escapes spaces within an option. The @code{$RCSINIT} options are prepended to the argument lists of most @sc{rcs} commands. - -@cindex TMPDIR -@item $TMPDIR -@cindex TMP -@itemx $TMP -@cindex TEMP -@itemx $TEMP -Name of the temporary directory. The environment -variables are inspected in the order they appear above -and the first value found is taken; if none of them are -set, a host-dependent default is used, typically -@file{/tmp}. @end table @c --------------------------------------------------------------------- @@ -7650,7 +10958,7 @@ set, a host-dependent default is used, typically @appendix Troubleshooting @menu -* Magic branch numbers:: Magic branch numbers +* Error messages:: Partial list of CVS errors @end menu @ignore @@ -7661,64 +10969,567 @@ set, a host-dependent default is used, typically @c -- Give hints on how to fix them @end ignore -@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@node Magic branch numbers -@appendixsec Magic branch numbers +@node Error messages +@appendixsec Partial list of error messages -Externally, branch numbers consist of an odd number of -dot-separated decimal integers. @xref{Revision -numbers}. That is not the whole truth, however. For -efficiency reasons @sc{cvs} sometimes inserts an extra 0 -in the second rightmost position (1.2.3 becomes -1.2.0.3, 8.9.10.11.12 becomes 8.9.10.11.0.12 and so -on). +Here is a partial list of error messages that you may +see from @sc{cvs}. It is not a complete list---@sc{cvs} +is capable of printing many, many error messages, often +with parts of them supplied by the operating system, +but the intention is to list the common and/or +potentially confusing error messages. -@sc{cvs} does a pretty good job at hiding these so -called magic branches, but in at least four places the -hiding is incomplete. +The messages are alphabetical, but introductory text +such as @samp{cvs update: } is not considered in +ordering them. -@itemize @bullet +In some cases the list includes messages printed by old +versions of @sc{cvs} (partly because users may not be +sure which version of @sc{cvs} they are using at any +particular moment). + +@table @code +@c FIXME: What is the correct way to format a multiline +@c error message here? Maybe @table is the wrong +@c choice? Texinfo gurus? +@item cannot change permissions on temporary directory +@example +Operation not permitted +@end example +This message has been happening in a non-reproducible, +occasional way when we run the client/server testsuite, +both on Red Hat Linux 3.0.3 and 4.1. We haven't been +able to figure out what causes it, nor is it known +whether it is specific to linux (or even to this +particular machine!). If the problem does occur on +other unices, @samp{Operation not permitted} would be +likely to read @samp{Not owner} or whatever the system +in question uses for the unix @code{EPERM} error. If +you have any information to add, please let us know as +described in @ref{BUGS}. If you experience this error +while using @sc{cvs}, retrying the operation which +produced it should work fine. +@c Most recently this was in the multibranch-5 test. +@c But I'm not sure it is specific to that test. + +@c For one example see basica-1a10 in the testsuite +@c For another example, "cvs co ." on NT; see comment +@c at windows-NT/filesubr.c (expand_wild). +@item cannot open CVS/Entries for reading: No such file or directory +This generally indicates a @sc{cvs} internal error, and +can be handled as with other @sc{cvs} bugs +(@pxref{BUGS}). Usually there is a workaround---the +exact nature of which would depend on the situation but +which hopefully could be figured out. + +@item cvs [update aborted]: could not patch @var{file}: No such file or directory +This means that there was a problem finding the +@code{patch} program. Make sure that it is in your +@code{PATH}. Note that despite appearances the message +is @emph{not} referring to whether it can find @var{file}. +@c Future versions of @sc{cvs} are +@c expected to dispense with the need for an external +@c patch program, but might as well not advertise +@c vaporware. +@c Even after that change is made, probably want to +@c preserve this message, see above about old messages. + +@item cvs update: could not patch @var{file}; will refetch +This means that for whatever reason the client was +unable to apply a patch that the server sent. The +message is nothing to be concerned about, because +inability to apply the patch only slows things down and +has no effect on what @sc{cvs} does. +@c xref to update output. Or File status? +@c Or some place else that +@c explains this whole "patch"/P/Needs Patch thing? + +@item dying gasps from @var{server} unexpected +This message seems to be caused by a hard-to-track-down +bug in @sc{cvs} or the systems it runs on (we don't +know---we haven't tracked it down yet!). If you see it, +you probably can just retry the operation which failed, +or if you have discovered information concerning its +cause, please let us know as described in @ref{BUGS}. + +@item end of file from server (consult above messages if any) +The most common cause for this message is if you are +using an external @code{rsh} program and it exited with +an error. In this case the @code{rsh} program should +have printed a message, which will appear before the +above message. For more information on setting up a +@sc{cvs} client and server, see @ref{Remote repositories}. + +@cindex mkmodules +@item cvs commit: Executing 'mkmodules' +This means that your repository is set up for a version +of @sc{cvs} prior to @sc{cvs} 1.8. When using @sc{cvs} +1.8 or later, the above message will be preceded by + +@example +cvs commit: Rebuilding administrative file database +@end example + +If you see both messages, the database is being rebuilt +twice, which is unnecessary but harmless. If you wish +to avoid the duplication, and you have no versions of +@sc{cvs} 1.7 or earlier in use, remove @code{-i mkmodules} +every place it appears in your @code{modules} +file. For more information on the @code{modules} file, +see @ref{modules}. + +@item cvs [server aborted]: received broken pipe signal +This message seems to be caused by a hard-to-track-down +bug in @sc{cvs} or the systems it runs on (we don't +know---we haven't tracked it down yet!). It seems to +happen only after a @sc{cvs} command has completed, and +you should be able to just ignore the message. +However, if you have discovered information concerning its +cause, please let us know as described in @ref{BUGS}. + +@item cvs commit: Up-to-date check failed for `@var{file}' +This means that someone else has committed a change to +that file since the last time that you did a @code{cvs +update}. So before proceeding with your @code{cvs +commit} you need to @code{cvs update}. CVS will merge +the changes that you made and the changes that the +other person made. If it does not detect any conflicts +it will report @samp{M cacErrCodes.h} and you are ready +to @code{cvs commit}. If it detects conflicts it will +print a message saying so, will report @samp{C cacErrCodes.h}, +and you need to manually resolve the +conflict. For more details on this process see +@ref{Conflicts example}. + +@item Usage: diff3 [-exEX3 [-i | -m] [-L label1 -L label3]] file1 file2 file3 +@example +Only one of [exEX3] allowed +@end example +This indicates a problem with the installation of +@code{diff3} and @code{rcsmerge}. Specifically +@code{rcsmerge} was compiled to look for GNU diff3, but +it is finding unix diff3 instead. The exact text of +the message will vary depending on the system. The +solution is to make sure @code{rcsmerge} finds GNU +diff3. Depending on how @code{rcsmerge} was compiled, +it might be sufficient to place GNU diff3 in your +@code{PATH}, or it might be necessary to recompile +@code{rcsmerge} or find a binary distribution of +@code{rcsmerge} which looks in the @code{PATH}. +@c Should we mention the cvsaux binaries here? + +@item cvs commit: warning: editor session failed +This means that the editor which @sc{cvs} is using exits with a nonzero +exit status. Some versions of vi will do this even when there was not +a problem editing the file. If so, point the +@sc{CVSEDITOR} environment variable to a small script +such as: + +@example +#!/bin/sh +vi $* +exit 0 +@end example + +@c "warning: foo was lost" and "no longer pertinent" (both normal). +@c Would be nice to write these up--they are +@c potentially confusing for the new user. +@end table + +@c --------------------------------------------------------------------- +@node Copying +@appendix GNU GENERAL PUBLIC LICENSE +@center Version 2, June 1991 + +@display +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@unnumberedsec Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@heading TERMS AND CONDITIONS FOR COPYING, +@heading DISTRIBUTION AND MODIFICATION +@end iftex +@ifinfo +@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end ifinfo + +@enumerate 0 @item -The magic branch can appear in the output from -@code{cvs status} in vanilla @sc{cvs} 1.3. This is -fixed in @sc{cvs} 1.3-s2. +This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The ``Program'', below, +refers to any such program or work, and a ``work based on the Program'' +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term ``modification''.) Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. @item -The magic branch number appears in the output from -@code{cvs log}. This is much harder to fix, since -@code{cvs log} runs @code{rlog} (which is part of the -@sc{rcs} distribution), and modifying @code{rlog} to -know about magic branches would probably break someone's -habits (if they use branch 0 for their own purposes). +You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. @item -You cannot specify a symbolic branch name to @code{cvs log}. +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: +@enumerate a @item -You cannot specify a symbolic branch name to @code{cvs -admin}. +You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. -@end itemize +@item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. -You can use the @code{admin} command to reassign a -symbolic name to a branch the way @sc{rcs} expects it -to be. If @code{R4patches} is assigned to the branch -1.4.2 (magic branch number 1.4.0.2) in file -@file{numbers.c} you can do this: +@item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) +@end enumerate -@example -$ cvs admin -NR4patches:1.4.2 numbers.c -@end example +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. -It only works if at least one revision is already -committed on the branch. Be very careful so that you -do not assign the tag to the wrong number. (There is -no way to see how the tag was assigned yesterday). +@item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: -@c --------------------------------------------------------------------- -@node Copying -@appendix GNU GENERAL PUBLIC LICENSE -@c @include gpl.texinfo +@enumerate a +@item +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +@item +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +@item +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) +@end enumerate + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY +@end ifinfo + +@item +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@page +@unnumberedsec How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) 19@var{yy} @var{name of author} + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details +type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than @samp{show w} and +@samp{show c}; they could even be mouse-clicks or menu items---whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +@smallexample +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end smallexample + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. @c --------------------------------------------------------------------- @node Index diff --git a/contrib/cvs/doc/cvsclient.texi b/contrib/cvs/doc/cvsclient.texi index 0d61ac1..0d80788 100644 --- a/contrib/cvs/doc/cvsclient.texi +++ b/contrib/cvs/doc/cvsclient.texi @@ -9,13 +9,12 @@ This document describes the client/server protocol used by CVS. It does not describe how to use or administer client/server CVS; see the regular CVS manual for that. This is version @value{CVSVN} of the protocol -specification---@xref{Introduction} for more on what this version number +specification---@xref{Introduction}, for more on what this version number means. @menu * Introduction:: What is CVS and what is the client/server protocol for? * Goals:: Basic design decisions, requirements, scope, etc. -* Notes:: Notes on the current implementation * Protocol Notes:: Possible enhancements, limitations, etc. of the protocol * Connection and Authentication:: Various ways to connect to the server * Protocol:: Complete description of the protocol @@ -60,7 +59,7 @@ versions of this specification. Although the specification is currently maintained in conjunction with the CVS implementation, and carries the same version number, it also intends to document what is involved with interoperating with other implementations (such as other versions of -CVS); see @xref{Requirements}. This version number should not be used +CVS); see @ref{Requirements}. This version number should not be used by clients or servers to determine what variant of the protocol to speak; they should instead use the @code{valid-requests} and @code{Valid-responses} mechanism (@pxref{Protocol}), which is more @@ -75,62 +74,45 @@ Do not assume any access to the repository other than via this protocol. It does not depend on NFS, rdist, etc. @item -Providing a reliable transport is outside this protocol. It is expected -that it runs over TCP, UUCP, etc. +Providing a reliable transport is outside this protocol. The protocol +expects a reliable transport that is transparent (that is, there is no +translation of characters, including characters such as such as +linefeeds or carriage returns), and can transmit all 256 octets (for +example for proper handling of binary files, compression, and +encryption). The encoding of characters specified by the protocol (the +names of requests and so on) is the invariant ISO 646 character set (a +subset of most popular character sets including ASCII and others). For +more details on running the protocol over the TCP reliable transport, +see @ref{Connection and Authentication}. @item Security and authentication are handled outside this protocol (but see -below about @samp{cvs kserver}). +below about @samp{cvs kserver} and @samp{cvs pserver}). @item -This might be a first step towards adding transactions to CVS (i.e. a -set of operations is either executed atomically or none of them is -executed), improving the locking, or other features. The current server -implementation is a long way from being able to do any of these -things. The protocol, however, is not known to contain any defects -which would preclude them. +The protocol makes it possible for updates to be atomic with respect to +checkins; that is if someone commits changes to several files in one cvs +command, then an update by someone else would either get all the +changes, or none of them. The current @sc{cvs} server can't do this, +but that isn't the protocol's fault. @item -The server never has to have any CVS locks in place while it is waiting -for communication with the client. This makes things robust in the face -of flaky networks. - -@item -Data is transferred in large chunks, which is necessary for good -performance. In fact, currently the client uploads all the data -(without waiting for server responses), and then waits for one server -response (which consists of a massive download of all the data). There -may be cases in which it is better to have a richer interraction, but -the need for the server to release all locks whenever it waits for the -client makes it complicated. +The protocol is, with a few exceptions, transaction-based. That is, the +client sends all its requests (without waiting for server responses), +and then waits for the server to send back all responses (without +waiting for further client requests). This has the advantage of +minimizing network turnarounds and the disadvantage of sometimes +transferring more data than would be necessary if there were a richer +interaction. Another, more subtle, advantage is that there is no need +for the protocol to provide locking for features such as making checkins +atomic with respect to updates. Any such locking can be handled +entirely by the server. A good server implementation (such as the +current @sc{cvs} server) will make sure that it does not have any such +locks in place whenever it is waiting for communication with the client; +this prevents one client on a slow or flaky network from interfering +with the work of others. @end itemize -@node Notes -@chapter Notes on the Current Implementation - -The client is built in to the normal @code{cvs} program, triggered by a -@code{CVSROOT} variable containing a colon, for example -@code{cygnus.com:/rel/cvsfiles}. - -The client stores what is stored in checked-out directories (including -@file{CVS}). The way these are stored is totally compatible with -standard CVS. The server requires no storage other than the repository, -which also is totally compatible with standard CVS. - -The server is started by @code{cvs server}. There is no particularly -compelling reason for this rather than making it a separate program -which shares a lot of sources with cvs. - -The server can also be started by @code{cvs kserver}, in which case it -does an initial Kerberos authentication on stdin. If the authentication -succeeds, it subsequently runs identically to @code{cvs server}. - -The current server implementation can use up huge amounts of memory -when transmitting a lot of data over a slow link (i.e. the network is -slower than the server can generate the data). There is some -experimental code (see @code{SERVER_FLOWCONTROL} in options.h) which -should help significantly. - @node Protocol Notes @chapter Notes on the Protocol @@ -140,10 +122,10 @@ A number of enhancements are possible: @item The @code{Modified} request could be speeded up by sending diffs rather than entire files. The client would need some way to keep the version -of the file which was originally checked out, which would double client -disk space requirements or require coordination with editors (e.g. maybe -it could use emacs numbered backups). This would also allow local -operation of @code{cvs diff} without arguments. +of the file which was originally checked out; probably requiring the use +of "cvs edit" in this case is the most sensible course (the "cvs edit" +could be handled by a package like VC for emacs). This would also allow +local operation of @code{cvs diff} without arguments. @item Have the client keep a copy of some part of the repository. This allows @@ -153,8 +135,13 @@ the master copy at night (but if the master copy has been updated since the latest nightly re-sync, then it would read what it needs to from the master). -@item -Provide encryption using kerberos. +It isn't clear exactly how this should relate to a more general +multisite feature (in which one can modify the local copy even if the +network is down between the local and the master, and then they get +reconciled by a potentially manual process). Another variant of a +multisite feature would be where version history is cached to speed up +operations such as @code{cvs diff}, but in which checkins still must be +checked in to all sites, or to a master site. @item The current procedure for @code{cvs update} is highly sub-optimal if @@ -166,6 +153,38 @@ more, if changes in the repository mean it has to ask the client for more files), because it can't keep locks open while waiting for the network. Perhaps this whole thing is irrelevant if client-side repositories are implemented, and the rcsmerge is done by the client. + +@item +The fact that @code{pserver} requires an extra network turnaround in +order to perform authentication would be nice to avoid. This relates to +the issue of reporting errors; probably the clean solution is to defer +the error until the client has issued a request which expects a +response. To some extent this might relate to the next item (in terms +of how easy it is to skip a whole bunch of requests until we get to one +that expects a response). I know that the kerberos code doesn't wait in +this fashion, but that probably can cause network deadlocks and perhaps +future problems running over a transport which is more transaction +oriented than TCP. On the other hand I'm not sure it is wise to make +the client conduct a lengthy upload only to find there is an +authentication failure. + +@item +The protocol uses an extra network turnaround for protocol negotiation +(@code{valid-requests}). It might be nice to avoid this by having the +client be able to send requests and tell the server to ignore them if +they are unrecognized (different requests could produce a fatal error if +unrecognized). To do this there should be a standard syntax for +requests. For example, perhaps all future requests should be a single +line, with mechanisms analogous to @code{Argumentx}, or several requests +working together, to provide greater amounts of information. Or there +might be a standard mechanism for counted data (analogous to that used +by @code{Modified}) or continuation lines (like a generalized +@code{Argumentx}). It would be useful to compare what HTTP is planning +in this area; last I looked they were contemplating something called +Protocol Extension Protocol but I haven't looked at the relevant IETF +documents in any detail. Obviously, we want something as simple as +possible (but no simpler). + @end itemize @node Connection and Authentication @@ -175,7 +194,7 @@ Connection and authentication occurs before the CVS protocol itself is started. There are several ways to connect. @table @asis -@item rsh +@item server If the client has a way to execute commands on the server, and provide input to the commands and output from them, then it can connect that way. This could be the usual rsh (port 514) protocol, Kerberos rsh, @@ -202,7 +221,11 @@ implementation, by having inetd call "cvs pserver") which defaults to connects, sends the string @samp{BEGIN AUTH REQUEST}, a linefeed, the cvs root, a linefeed, the username, a linefeed, the password trivially encoded (see scramble.c in the cvs sources), a linefeed, the string -@samp{END AUTH REQUEST}, and a linefeed. The server responds with +@samp{END AUTH REQUEST}, and a linefeed. The client must send the +identical string for cvs root both here and later in the +@code{Root} request of the cvs +protocol itself. Servers are encouraged to enforce this restriction. +The server responds with @samp{I LOVE YOU} and a linefeed if the authentication is successful or @samp{I HATE YOU} and a linefeed if the authentication fails. After receiving @samp{I LOVE YOU}, the client proceeds with the cvs protocol. @@ -211,6 +234,17 @@ protocol, the procedure is the same, except @samp{BEGIN AUTH REQUEST} is replaced with @samp{BEGIN VERIFICATION REQUEST}, @samp{END AUTH REQUEST} is replaced with @samp{END VERIFICATION REQUEST}, and upon receipt of @samp{I LOVE YOU} the connection is closed rather than continuing. + +@item future possibilities +There are a nearly unlimited number of ways to connect and authenticate. +One might want to allow access based on IP address (similar to the usual +rsh protocol but with different/no restrictions on ports < 1024), to +adopt mechanisms such as the General Security Service (GSS) API or +Pluggable Authentication Modules (PAM), to allow users to run their own +servers under their own usernames without root access, or any number of +other possibilities. The way to add future mechanisms, for the most +part, should be to continue to use port 2401, but to use different +strings in place of @samp{BEGIN AUTH REQUEST}. @end table @node Protocol @@ -223,10 +257,13 @@ to a horizontal tab. * Entries Lines:: * Modes:: * Filenames:: Conventions regarding filenames +* File transmissions:: How file contents are transmitted +* Strings:: Strings in various requests and responses * Requests:: * Responses:: * Example:: * Requirements:: +* Obsolete:: Former protocol features @end menu @node Entries Lines @@ -245,6 +282,9 @@ shall be silently ignored. @var{version} can be empty, or start with @samp{0} or @samp{-}, for no user file, new user file, or user file to be removed, respectively. +@c FIXME: should distinguish sender and receiver behavior here; the +@c "anything else" and "does not start with" are intended for future +@c expansion, and we should specify a sender behavior. @var{conflict}, if it starts with @samp{+}, indicates that the file had conflicts in it. The rest of @var{conflict} is @samp{=} if the timestamp matches the file, or anything else if it doesn't. If @@ -299,30 +339,74 @@ the same answer (and what to do if future server ports are operating on a repository like e:/foo or CVS_ROOT:[FOO.BAR] has not been carefully thought out). -@node Requests -@section Requests +Characters outside the invariant ISO 646 character set should be avoided +in filenames. This restriction may need to be relaxed to allow for +characters such as @samp{[} and @samp{]} (see above about non-unix +servers); this has not been carefully considered (and currently +implementations probably use whatever character sets that the operating +systems they are running on allow, and/or that users specify). Of +course the most portable practice is to restrict oneself further, to the +POSIX portable filename character set as specified in POSIX.1. + +@node File transmissions +@section File transmissions File contents (noted below as @var{file transmission}) can be sent in one of two forms. The simpler form is a number of bytes, followed by a -newline, followed by the specified number of bytes of file contents. +linefeed, followed by the specified number of bytes of file contents. These are the entire contents of the specified file. Second, if both client and server support @samp{gzip-file-contents}, a @samp{z} may precede the length, and the `file contents' sent are actually compressed -with @samp{gzip}. The length specified is that of the compressed -version of the file. +with @samp{gzip} (RFC1952/1951) compression. The length specified is +that of the compressed version of the file. In neither case are the file content followed by any additional data. -The transmission of a file will end with a newline iff that file (or its -compressed form) ends with a newline. +The transmission of a file will end with a linefeed iff that file (or its +compressed form) ends with a linefeed. + +The encoding of file contents depends on the value for the @samp{-k} +option. If the file is binary (as specified by the @samp{-kb} option in +the appropriate place), then it is just a certain number of octets, and +the protocol contributes nothing towards determining the encoding (using +the file name is one widespread, if not universally popular, mechanism). +If the file is text (not binary), then the file is sent as a series of +lines, separated by linefeeds. If the keyword expansion is set to +something other than @samp{-ko}, then it is expected that the file +conform to the RCS expectations regarding keyword expansion---in +particular, that it is in a character set such as ASCII in which 0x24 is +a dollar sign (@samp{$}). + +@node Strings +@section Strings + +In various contexts, for example the @code{Argument} request and the +@code{M} response, one transmits what is essentially an arbitrary +string. Often this will have been supplied by the user (for example, +the @samp{-m} option to the @code{ci} request). The protocol has no +mechanism to specify the character set of such strings; it would be +fairly safe to stick to the invariant ISO 646 character set but the +existing practice is probably to just transmit whatever the user +specifies, and hope that everyone involved agrees which character set is +in use, or sticks to a common subset. + +@node Requests +@section Requests + +By convention, requests which begin with a capital letter do not elicit +a response from the server, while all others do -- save one. The +exception is @samp{gzip-file-contents}. Unrecognized requests will +always elicit a response from the server, even if that request begins +with a capital letter. @table @code @item Root @var{pathname} \n Response expected: no. Tell the server which @code{CVSROOT} to use. -@var{pathname} must already exist; if creating a new root, use the -@code{init} request, not @code{Root}. @var{pathname} does not include -the hostname of the server, how to access the server, etc.; by the time -the CVS protocol is in use, connection, authentication, etc., are -already taken care of. +Note that @var{pathname} is a local directory and @emph{not} a fully +qualified @code{CVSROOT} variable. @var{pathname} must +already exist; if creating a new root, use the @code{init} request, not +@code{Root}. @var{pathname} does not include the hostname of the +server, how to access the server, etc.; by the time the CVS protocol is +in use, connection, authentication, etc., are already taken care of. @item Valid-responses @var{request-list} \n Response expected: no. @@ -333,24 +417,83 @@ request-list is a space separated list of tokens. Response expected: yes. Ask the server to send back a @code{Valid-requests} response. -@item Repository @var{repository} \n -Response expected: no. Tell the server what repository to use. This -should be a directory name from a previous server response. Note that -this both gives a default for @code{Entry } and @code{Modified } and -also for @code{ci} and the other commands; normal usage is to send a -@code{Repository } for each directory in which there will be an -@code{Entry } or @code{Modified }, and then a final @code{Repository } -for the original directory, then the command. - @item Directory @var{local-directory} \n -Additional data: @var{repository} \n. This is like @code{Repository}, -but the local name of the directory may differ from the repository name. +Additional data: @var{repository} \n. Response expected: no. +Tell the server what directory to use. The @var{repository} should be a +directory name from a previous server response. Note that +this both gives a default for @code{Entry} and @code{Modified} and +also for @code{ci} and the other commands; normal usage is to send +@code{Directory} for each directory in which there will be an +@code{Entry} or @code{Modified}, and then a final @code{Directory} +for the original directory, then the command. If the client uses this request, it affects the way the server returns pathnames; see @ref{Responses}. @var{local-directory} is relative to the top level at which the command is occurring (i.e. the last -@code{Directory} or @code{Repository} which is sent before the command). +@code{Directory} which is sent before the command); +to indicate that top level, @samp{.} should be send for +@var{local-directory}. + +Here is an example of where a client gets @var{repository} and +@var{local-directory}. Suppose that there is a module defined by + +@example +moddir 1dir +@end example + +That is, one can check out @code{moddir} and it will take @code{1dir} in +the repository and check it out to @code{moddir} in the working +directory. Then an initial check out could proceed like this: + +@example +C: Root /home/kingdon/zwork/cvsroot +. . . +C: Argument moddir +C: Directory . +C: /home/kingdon/zwork/cvsroot +C: co +S: Clear-sticky moddir/ +S: /home/kingdon/zwork/cvsroot/1dir/ +. . . +S: ok +@end example + +In this example the response shown is @code{Clear-sticky}, but it could +be another response instead. Note that it returns two pathnames. +The first one, @file{moddir/}, indicates the working +directory to check out into. The second one, ending in @file{1dir/}, +indicates the directory to pass back to the server in a subsequent +@code{Directory} request. For example, a subsequent @code{update} +request might look like: + +@example +C: Directory moddir +C: /home/kingdon/zwork/cvsroot/1dir +. . . +C: update +@end example + +For a given @var{local-directory}, the repository will be the same for +each of the responses, so one can use the repository from whichever +response is most convenient. Typically a client will store the +repository along with the sources for each @var{local-directory}, use +that same setting whenever operating on that @var{local-directory}, and +not update the setting as long as the @var{local-directory} exists. + +A client is free to rename a @var{local-directory} at any time (for +example, in response to an explicit user request). While it is true +that the server supplies a @var{local-directory} to the client, as noted +above, this is only the default place to put the directory. Of course, +the various @code{Directory} requests for a single command (for example, +@code{update} or @code{ci} request) should name a particular directory +with the same @var{local-directory}. + +Each @code{Directory} request specifies a brand-new +@var{local-directory} and @var{repository}; that is, +@var{local-directory} and @var{repository} are never relative to paths +specified in any previous @code{Directory} request. @item Max-dotdot @var{level} \n +Response expected: no. Tell the server that @var{level} levels of directories above the directory which @code{Directory} requests are relative to will be needed. For example, if the client is planning to use a @@ -361,7 +504,7 @@ request. @item Static-directory \n Response expected: no. Tell the server that the directory most recently -specified with @code{Repository} or @code{Directory} should not have +specified with @code{Directory} should not have additional files checked out unless explicitly requested. The client sends this if the @code{Entries.Static} flag is set, which is controlled by the @code{Set-static-directory} and @code{Clear-static-directory} @@ -369,11 +512,17 @@ responses. @item Sticky @var{tagspec} \n Response expected: no. Tell the server that the directory most recently -specified with @code{Repository} has a sticky tag or date @var{tagspec}. +specified with @code{Directory} has a sticky tag or date @var{tagspec}. The first character of @var{tagspec} is @samp{T} for a tag, or @samp{D} for a date. The remainder of @var{tagspec} contains the actual tag or date. +The server should remember @code{Static-directory} and @code{Sticky} +requests for a particular directory; the client need not resend them +each time it sends a @code{Directory} request for a given directory. +However, the server is not obliged to remember them beyond the context +of a single command. + @item Checkin-prog @var{program} \n Response expected: no. Tell the server that the directory most recently specified with @code{Directory} has a checkin program @var{program}. @@ -389,44 +538,72 @@ Such a program would have been previously set with the @item Entry @var{entry-line} \n Response expected: no. Tell the server what version of a file is on the local machine. The name in @var{entry-line} is a name relative to the -directory most recently specified with @code{Repository}. If the user +directory most recently specified with @code{Directory}. If the user is operating on only some files in a directory, @code{Entry} requests for only those files need be included. If an @code{Entry} request is -sent without @code{Modified}, @code{Unchanged}, or @code{Lost} for that -file the meaning depends on whether @code{UseUnchanged} has been sent; -if it has been it means the file is lost, if not it means the file is -unchanged. +sent without @code{Modified}, @code{Is-modified}, or @code{Unchanged}, +it means the file is +lost (does not exist in the working directory). If both @code{Entry} +and one of @code{Modified}, @code{Is-modified}, or @code{Unchanged} are +sent for the same file, @code{Entry} must be sent first. For a +given file, one can send @code{Modified}, @code{Is-modified}, or +@code{Unchanged}, but not more than one of these three. @item Modified @var{filename} \n Response expected: no. Additional data: mode, \n, file transmission. Send the server a copy of one locally modified file. @var{filename} is -relative to the most recent repository sent with @code{Repository}. If +relative to the most recent repository sent with @code{Directory}. If the user is operating on only some files in a directory, only those files need to be included. This can also be sent without @code{Entry}, if there is no entry for the file. -@item Lost @var{filename} \n -Response expected: no. Tell the server that @var{filename} no longer -exists. The name is relative to the most recent repository sent with -@code{Repository}. This is used for any case in which @code{Entry} is -being sent but the file no longer exists. If the client has issued the -@code{UseUnchanged} request, then this request is not used. +@item Is-modified @var{filename} \n +Response expected: no. Additional data: none. Like @code{Modified}, +but used if the server only needs +to know whether the file is modified, not the contents. + +The commands which can take @code{Is-modified} instead of +@code{Modified} with no known change in behavior are: @code{admin}, +@code{diff} (if and only if two @samp{-r} or @samp{-D} options are +specified), @code{watch-on}, @code{watch-off}, @code{watch-add}, +@code{watch-remove}, @code{watchers}, @code{editors}, +@code{log}, and @code{annotate}. + +For the @code{status} command, one can send @code{Is-modified} but if +the client is using imperfect mechanisms such as timestamps to determine +whether to consider a file modified, then the behavior will be +different. That is, if one sends @code{Modified}, then the server will +actually compare the contents of the file sent and the one it derives +from to determine whether the file is genuinely modified. But if one +sends @code{Is-modified}, then the server takes the client's word for +it. A similar situation exists for @code{tag}, if the @samp{-c} option +is specified. + +Commands for which @code{Modified} is necessary are @code{co}, +@code{ci}, @code{update}, and @code{import}. + +Commands which do not need to inform the server about a working +directory, and thus should not be sending either @code{Modified} or +@code{Is-modified}: @code{rdiff}, @code{rtag}, @code{history}, +@code{init}, and @code{release}. + +Commands for which further investigation is warranted are: +@code{remove}, @code{add}, and @code{export}. Pending such +investigation, the more conservative course of action is to stick to +@code{Modified}. @item Unchanged @var{filename} \n Response expected: no. Tell the server that @var{filename} has not been modified in the checked out directory. The name is relative to the most -recent repository sent with @code{Repository}. This request can only be -issued if @code{UseUnchanged} has been sent. +recent repository sent with @code{Directory}. @item UseUnchanged \n -Response expected: no. Tell the server that the client will be -indicating unmodified files with @code{Unchanged}, and that files for -which no information is sent are nonexistent on the client side, not -unchanged. This is necessary for correct behavior since only the server -knows what possible files may exist, and thus what files are -nonexistent. +Response expected: no. To specify the version of the protocol described +in this document, servers must support this request (although it need +not do anything) and clients must issue it. @item Notify @var{filename} \n +Response expected: no. Tell the server that a @code{edit} or @code{unedit} command has taken place. The server needs to send a @code{Notified} response, but such response is deferred until the next time that the server is sending @@ -435,26 +612,56 @@ responses. Response expected: no. Additional data: @var{notification-type} \t @var{time} \t @var{clienthost} \t @var{working-dir} \t @var{watches} \n @end example -where @var{notification-type} is @samp{E} for edit or @samp{U} for -unedit, @var{time} is the time at which the edit or unedit took place, +where @var{notification-type} is @samp{E} for edit, @samp{U} for +unedit, undefined behavior if @samp{C}, and all other letters should be +silently ignored for future expansion. +@var{time} is the time at which the edit or unedit took place, in a +user-readable format of the client's choice (the server should treat the +time as an opaque string rather than interpreting it). +@c Might be useful to specify a format, but I don't know if we want to +@c specify the status quo (ISO C asctime() format plus timezone) without +@c offering the option of ISO8601 and/or RFC822/1123 (see cvs.texinfo +@c for much much more on date formats). @var{clienthost} is the name of the host on which the edit or unedit took place, and @var{working-dir} is the pathname of the working directory where the edit or unedit took place. @var{watches} are the -temporary watches to set; if it is followed by \t then the tab and the -rest of the line are ignored. +temporary watches to set. If @var{watches} is followed by \t then the +\t and the rest of the line should be ignored, for future expansion. + +Note that a client may be capable of performing an @code{edit} or +@code{unedit} operation without connecting to the server at that time, +and instead connecting to the server when it is convenient (for example, +when a laptop is on the net again) to send the @code{Notify} requests. +Even if a client is capable of deferring notifications, it should +attempt to send them immediately (one can send @code{Notify} requests +together with a @code{noop} request, for example), unless perhaps if +it can know that a connection would be impossible. @item Questionable @var{filename} \n Response expected: no. Additional data: no. Tell the server to check whether @var{filename} should be ignored, and if not, next time the server sends responses, send (in a @code{M} response) @samp{?} followed -by the directory and filename. +by the directory and filename. @var{filename} must not contain +@samp{/}; it needs to be a file in the directory named by the most +recent @code{Directory} request. +@c FIXME: the bit about not containing / is true of most of the +@c requests, but isn't documented and should be. @item Case \n -Tell the server that filenames should be matched against ignore patterns -in a case-insensitive fashion. Note that this does not apply to other -comparisons---for example the filenames given in @code{Entry} and -@code{Modified} requests for the same file must match in case regardless -of whether the @code{Case} request is sent. +Response expected: no. Tell the server that filenames should be matched +in a case-insensitive fashion. Note that this is not the primary +mechanism for achieving case-insensitivity; for the most part the client +keeps track of the case which the server wants to use and takes care to +always use that case regardless of what the user specifies. For example +the filenames given in @code{Entry} and @code{Modified} requests for the +same file must match in case regardless of whether the @code{Case} +request is sent. The latter mechanism is more general (it could also be +used for 8.3 filenames, VMS filenames with more than one @samp{.}, and +any other situation in which there is a predictable mapping between +filenames in the working directory and filenames in the protocol), but +there are some situations it cannot handle (ignore patterns, or +situations where the user specifies a filename and the client does not +know about that file). @item Argument @var{text} \n Response expected: no. @@ -467,6 +674,7 @@ Response expected: no. Append \n followed by text to the current argument being saved. @item Global_option @var{option} \n +Response expected: no. Transmit one of the global options @samp{-q}, @samp{-Q}, @samp{-l}, @samp{-t}, @samp{-r}, or @samp{-n}. @var{option} must be one of those strings, no variations (such as combining of options) are allowed. For @@ -474,7 +682,29 @@ graceful handling of @code{valid-requests}, it is probably better to make new global options separate requests, rather than trying to add them to this request. +@item Gzip-stream @var{level} \n +Response expected: no. +Use zlib (RFC 1950/1951) compression to compress all further communication +between the client and the server. After this request is sent, all +further communication must be compressed. All further data received +from the server will also be compressed. The @var{level} argument +suggests to the server the level of compression that it should apply; it +should be an integer between 1 and 9, inclusive, where a higher number +indicates more compression. + +@item Kerberos-encrypt \n +Response expected: no. +Use Kerberos encryption to encrypt all further communication between the +client and the server. This will only work if the connection was made +over Kerberos in the first place. If both the @code{Gzip-stream} and +the @code{Kerberos-encrypt} requests are used, the +@code{Kerberos-encrypt} request should be used first. This will make +the client and server encrypt the compressed data, as opposed to +compressing the encrypted data. Encrypted data is generally +incompressible. + @item Set @var{variable}=@var{value} \n +Response expected: no. Set a user variable @var{variable} to @var{value}. @item expand-modules \n @@ -484,16 +714,57 @@ that the server can assume that this is checkout or export, not rtag or rdiff; the latter do not access the working directory and thus have no need to expand modules on the client side. -@item co \n -@itemx ci \n +Expand may not be the best word for what this request does. It does not +necessarily tell you all the files contained in a module, for example. +Basically it is a way of telling you which working directories the +server needs to know about in order to handle a checkout of the +specified modules. + +For example, suppose that the server has a module defined by + +@example +aliasmodule -a 1dir +@end example + +That is, one can check out @code{aliasmodule} and it will take +@code{1dir} in the repository and check it out to @code{1dir} in the +working directory. Now suppose the client already has this module +checked out and is planning on using the @code{co} request to update it. +Without using @code{expand-modules}, the client would have two bad +choices: it could either send information about @emph{all} working +directories under the current directory, which could be unnecessarily +slow, or it could be ignorant of the fact that @code{aliasmodule} stands +for @code{1dir}, and neglect to send information for @code{1dir}, which +would lead to incorrect operation. +@c Those don't really seem like the only two options. I mean, what +@c about keeping track of the correspondence from when we first checked +@c out a fresh directory? Not that the CVS client does this, or that +@c I've really thought about whether it would be a good idea... + +With @code{expand-modules}, the client would first ask for the module to +be expanded: + +@example +C: Root /home/kingdon/zwork/cvsroot +. . . +C: Argument aliasmodule +C: Directory . +C: /home/kingdon/zwork/cvsroot +C: expand-modules +S: Module-expansion 1dir +S: ok +@end example + +and then it knows to check the @file{1dir} directory and send +requests such as @code{Entry} and @code{Modified} for the files in that +directory. + +@item ci \n @itemx diff \n @itemx tag \n @itemx status \n @itemx log \n -@itemx add \n @itemx remove \n -@itemx rdiff \n -@itemx rtag \n @itemx admin \n @itemx export \n @itemx history \n @@ -501,38 +772,128 @@ need to expand modules on the client side. @itemx editors \n @itemx annotate \n Response expected: yes. Actually do a cvs command. This uses any -previous @code{Argument}, @code{Repository}, @code{Entry}, -@code{Modified}, or @code{Lost} requests, if they have been sent. The -last @code{Repository} sent specifies the working directory at the time +previous @code{Argument}, @code{Directory}, @code{Entry}, or +@code{Modified} requests, if they have been sent. The +last @code{Directory} sent specifies the working directory at the time of the operation. No provision is made for any input from the user. This means that @code{ci} must use a @code{-m} argument if it wants to specify a log message. -@itemx init @var{root-name} \n +@item co \n +Response expected: yes. Get files from the repository. This uses any +previous @code{Argument}, @code{Directory}, @code{Entry}, or +@code{Modified} requests, if they have been sent. Arguments to this +command are module names; the client cannot know what directories they +correspond to except by (1) just sending the @code{co} request, and then +seeing what directory names the server sends back in its responses, and +(2) the @code{expand-modules} request. + +@item rdiff \n +@itemx rtag \n +Response expected: yes. Actually do a cvs command. This uses any +previous @code{Argument} requests, if they have been sent. The client +should not send @code{Directory}, @code{Entry}, or @code{Modified} +requests for this command; they are not used. Arguments to these +commands are module names, as described for @code{co}. + +@item init @var{root-name} \n Response expected: yes. If it doesn't already exist, create a @sc{cvs} -repository @var{root-name}. The @code{Root} request need not have been -previously sent. +repository @var{root-name}. Note that @var{root-name} is a local +directory and @emph{not} a fully qualified @code{CVSROOT} variable. The +@code{Root} request need not have been previously sent. -@itemx update \n +@item update \n Response expected: yes. Actually do a @code{cvs update} command. This -uses any previous @code{Argument}, @code{Repository}, @code{Entry}, -@code{Modified}, or @code{Lost} requests, if they have been sent. The -last @code{Repository} sent specifies the working directory at the time +uses any previous @code{Argument}, @code{Directory}, @code{Entry}, +or @code{Modified} requests, if they have been sent. The +last @code{Directory} sent specifies the working directory at the time of the operation. The @code{-I} option is not used--files which the client can decide whether to ignore are not mentioned and the client sends the @code{Questionable} request for others. @item import \n Response expected: yes. Actually do a @code{cvs import} command. This -uses any previous @code{Argument}, @code{Repository}, @code{Entry}, -@code{Modified}, or @code{Lost} requests, if they have been sent. The -last @code{Repository} sent specifies the working directory at the time +uses any previous @code{Argument}, @code{Directory}, @code{Entry}, or +@code{Modified} requests, if they have been sent. The +last @code{Directory} sent specifies the working directory at the time of the operation. The files to be imported are sent in @code{Modified} requests (files which the client knows should be ignored are not sent; the server must still process the CVSROOT/cvsignore file unless -I ! is sent). A log message must have been specified with a @code{-m} argument. +@item add \n +Response expected: yes. Add a file or directory. This uses any +previous @code{Argument}, @code{Directory}, @code{Entry}, or +@code{Modified} requests, if they have been sent. The +last @code{Directory} sent specifies the working directory at the time +of the operation. + +To add a directory, send the directory to be added using +@code{Directory} and @code{Argument} requests. For example: + +@example +C: Root /u/cvsroot +. . . +C: Argument nsdir +C: Directory nsdir +C: /u/cvsroot/1dir/nsdir +C: Directory . +C: /u/cvsroot/1dir +C: add +S: M Directory /u/cvsroot/1dir/nsdir added to the repository +S: ok +@end example + +You will notice that the server does not signal to the client in any +particular way that the directory has been successfully added. The +client is supposed to just assume that the directory has been added and +update its records accordingly. Note also that adding a directory is +immediate; it does not wait until a @code{ci} request as files do. + +To add a file, send the file to be added using a @code{Modified} +request. For example: + +@example +C: Argument nfile +C: Directory . +C: /u/cvsroot/1dir +C: Modified nfile +C: u=rw,g=r,o=r +C: 6 +C: hello +C: add +S: E cvs server: scheduling file `nfile' for addition +S: Mode u=rw,g=r,o=r +S: Checked-in ./ +S: /u/cvsroot/1dir/nfile +S: /nfile/0/// +S: E cvs server: use 'cvs commit' to add this file permanently +S: ok +@end example + +Note that the file has not been added to the repository; the only effect +of a successful @code{add} request, for a file, is to supply the client +with a new entries line containing @samp{0} to indicate an added file. +In fact, the client probably could perform this operation without +contacting the server, although using @code{add} does cause the server +to perform a few more checks. + +The client sends a subsequent @code{ci} to actually add the file to the +repository. + +Another quirk of the @code{add} request is that a pathname specified in +an @code{Argument} request cannot contain @samp{/}. There is no good +reason for this restriction, and it could be eliminated if someone took +the effort to rewrite the @code{add} code in the CVS server to not have +it. But in the meantime, the way to comply with it is to ensure that +all @code{Directory} requests for @code{add} (except those used to add +directories, as described above), use @samp{.} for +@var{local-directory}. Specifying another string for +@var{local-directory} may not get an error, but it will get you strange +@code{Checked-in} responses, until servers are fixed to send the correct +responses. + @item watch-on \n @itemx watch-off \n @itemx watch-add \n @@ -540,8 +901,8 @@ argument. Response expected: yes. Actually do the @code{cvs watch on}, @code{cvs watch off}, @code{cvs watch add}, and @code{cvs watch remove} commands, respectively. This uses any previous @code{Argument}, -@code{Repository}, @code{Entry}, @code{Modified}, or @code{Lost} -requests, if they have been sent. The last @code{Repository} sent +@code{Directory}, @code{Entry}, or @code{Modified} +requests, if they have been sent. The last @code{Directory} sent specifies the working directory at the time of the operation. @item release \n @@ -555,16 +916,21 @@ expecting a response) sends back any responses pertaining to pending errors, pending @code{Notified} responses, etc. @item update-patches \n +Response expected: yes. This request does not actually do anything. It is used as a signal that the server is able to generate patches when given an @code{update} request. The client must issue the @code{-u} argument to @code{update} in order to receive patches. @item gzip-file-contents @var{level} \n -This request asks the server to filter files it sends to the client -through the @samp{gzip} program, using the specified level of -compression. If this request is not made, the server must not do any -compression. +Response expected: no. Note that this request does not follow the +response convention stated above. @code{Gzip-stream} is suggested +instead of @code{gzip-file-contents} as it gives better compression; the +only reason to implement the latter is to provide compression with +@sc{cvs} 1.8 and earlier. The @code{gzip-file-contents} request asks +the server to compress files it sends to the client using @code{gzip} +(RFC1952/1951) compression, using the specified level of compression. +If this request is not made, the server must not compress files. This is only a hint to the server. It may still decide (for example, in the case of very small files, or files that already appear to be @@ -589,23 +955,52 @@ When the client is done, it drops the connection. @section Responses After a command which expects a response, the server sends however many -of the following responses are appropriate. Pathnames are of the actual -files operated on (i.e. they do not contain @samp{,v} endings), and are -suitable for use in a subsequent @code{Repository} request. However, if -the client has used the @code{Directory} request, then it is instead a -local directory name relative to the directory in which the command was -given (i.e. the last @code{Directory} before the command). Then a -newline and a repository name (the pathname which is sent if -@code{Directory} is not used). Then the slash and the filename. For -example, for a file @file{i386.mh} which is in the local directory -@file{gas.clean/config} and for which the repository is -@file{/rel/cvsfiles/devo/gas/config}: +of the following responses are appropriate. The server should not send +data at other times (the current implementation may violate this +principle in a few minor places, where the server is printing an error +message and exiting---this should be investigated further). + +@c FIXME: should better document when the specified repository needs to +@c end in "/.". +In the following, @var{pathname} actually indicates a pair of +pathnames. First, a local directory name +relative to the directory in which the command was given (i.e. the last +@code{Directory} before the command). Then a linefeed and a repository +name. Then +a slash and the filename (without a @samp{,v} ending). +For example, for a file @file{i386.mh} +which is in the local directory @file{gas.clean/config} and for which +the repository is @file{/rel/cvsfiles/devo/gas/config}: @example gas.clean/config/ /rel/cvsfiles/devo/gas/config/i386.mh @end example +If the server wants to tell the client to create a directory, then it +merely uses the directory in any response, as described above, and the +client should create the directory if it does not exist. Note that this +should only be done one directory at a time, in order to permit the +client to correctly store the repository for each directory. Servers +can use requests such as @code{Clear-sticky}, +@code{Clear-static-directory}, or any other requests, to create +directories. +@c FIXME: Need example here of how "repository" needs to be sent for +@c each directory, and cannot be correctly deduced from, say, the most +@c deeply nested directory. + +Some server +implementations may poorly distinguish between a directory which should +not exist and a directory which contains no files; in order to refrain +from creating empty directories a client should both send the @samp{-P} +option to @code{update} or @code{co}, and should also detect the case in +which the server asks to create a directory but not any files within it +(in that case the client should remove the directory or refrain from +creating it in the first place). Note that servers could clean this up +greatly by only telling the client to create directories if the +directory in question should exist, but until servers do this, clients +will need to offer the @samp{-P} behavior described above. + Any response always ends with @samp{error} or @samp{ok}. This indicates that the response is over. @@ -631,7 +1026,34 @@ new copy of the file is enclosed. This is used for a new revision of an existing file, or for a new file, or for any other case in which the local (client-side) copy of the file needs to be updated, and after being updated it will be up to date. If any directory in pathname does -not exist, create it. +not exist, create it. This response is not used if @code{Created} and +@code{Update-existing} are supported. + +@item Created @var{pathname} \n +This is just like @code{Updated} and takes the same additional data, but +is used only if no @code{Entry}, @code{Modified}, or +@code{Unchanged} request has been sent for the file in question. The +distinction between @code{Created} and @code{Update-existing} is so +that the client can give an error message in several cases: (1) there is +a file in the working directory, but not one for which @code{Entry}, +@code{Modified}, or @code{Unchanged} was sent (for example, a file which +was ignored, or a file for which @code{Questionable} was sent), (2) +there is a file in the working directory whose name differs from the one +mentioned in @code{Created} in ways that the client is unable to use to +distinguish files. For example, the client is case-insensitive and the +names differ only in case. + +@item Update-existing @var{pathname} \n +This is just like @code{Updated} and takes the same additional data, but +is used only if a @code{Entry}, @code{Modified}, or @code{Unchanged} +request has been sent for the file in question. + +This response, or @code{Merged}, indicates that the server has +determined that it is OK to overwrite the previous contents of the file +specified by @var{pathname}. Provided that the client has correctly +sent @code{Modified} or @code{Is-modified} requests for a modified file, +and the file was not modified while CVS was running, the server can +ensure that a user's modifications are not lost. @item Merged @var{pathname} \n This is just like @code{Updated} and takes the same additional data, @@ -639,14 +1061,38 @@ with the one difference that after the new copy of the file is enclosed, it will still not be up to date. Used for the results of a merge, with or without conflicts. -@item Patched @var{pathname} \n +It is useful to preserve an copy of what the file looked like before the +merge. This is basically handled by the server; before sending +@code{Merged} it will send a @code{Copy-file} response. For example, if +the file is @file{aa} and it derives from revision 1.3, the +@code{Copy-file} response will tell the client to copy @file{aa} to +@file{.#aa.1.3}. It is up to the client to decide how long to keep this +file around; traditionally clients have left it around forever, thus +letting the user clean it up as desired. But another answer, such as +until the next commit, might be preferable. + +@item Rcs-diff @var{pathname} \n This is just like @code{Updated} and takes the same additional data, with the one difference that instead of sending a new copy of the file, -the server sends a patch produced by @samp{diff -u}. This client must -apply this patch, using the @samp{patch} program, to the existing file. -This will only be used when the client has an exact copy of an earlier -revision of a file. This response is only used if the @code{update} -command is given the @samp{-u} argument. +the server sends an RCS change text. This change text is produced by +@samp{diff -n} (the GNU diff @samp{-a} option may also be used). The +client must apply this change text to the existing file. This will only +be used when the client has an exact copy of an earlier revision of a +file. This response is only used if the @code{update} command is given +the @samp{-u} argument. + +@item Patched @var{pathname} \n +This is just like @code{Rcs-diff} and takes the same additional data, +except that it sends a standard patch rather than an RCS change text. +The patch is produced by @samp{diff -c} for @sc{cvs} 1.6 and later (see +POSIX.2 for a description of this format), or @samp{diff -u} for +previous versions of @sc{cvs}; clients are encouraged to accept either +format. Like @code{Rcs-diff}, this response is only used if the +@code{update} command is given the @samp{-u} argument. + +The @code{Patched} response is deprecated in favor of the +@code{Rcs-diff} response. However, older clients (CVS 1.9 and earlier) +only support @code{Patched}. @item Mode @var{mode} \n This @var{mode} applies to the next file mentioned in @@ -669,6 +1115,12 @@ Additional data: @var{newname} \n. Copy file @var{pathname} to @var{newname} in the same directory where it already is. This does not affect @code{CVS/Entries}. +This can optionally be implemented as a rename instead of a copy. The +only use for it which currently has been identified is prior to a +@code{Merged} response as described under @code{Merged}. Clients can +probably assume that is how it is being used, if they want to worry +about things like how long to keep the @var{newname} file around. + @item Removed @var{pathname} \n The file has been removed from the repository (this is the case where cvs prints @samp{file foobar.c is no longer pertinent}). @@ -692,8 +1144,10 @@ Like @code{Set-static-directory}, but clear, not set, the flag. Additional data: @var{tagspec} \n. Tell the client to set a sticky tag or date, which should be supplied with the @code{Sticky} request for future operations. @var{pathname} ends in a slash; its purpose is to -specify a directory, not a file within a directory. The first character -of @var{tagspec} is @samp{T} for a tag, or @samp{D} for a date. The +specify a directory, not a file within a directory. The client should +store @var{tagspec} and pass it back to the server as-is, to allow for +future expansion. The first character of @var{tagspec} is @samp{T} for +a tag, @samp{D} for a date, or something else for future expansion. The remainder of @var{tagspec} contains the actual tag or date. @item Clear-sticky @var{pathname} \n @@ -738,6 +1192,14 @@ A one-line message for the user. @item E @var{text} \n Same as @code{M} but send to stderr not stdout. +@item F \n +@c FIXME: The second sentence, defining "flush", is somewhat off the top +@c of my head. Is there some text we can steal from ANSI C or someplace +@c which is more carefully thought out? +Flush stderr. That is, make it possible for the user to see what has +been written to stderr (it is up to the implementation to decide exactly +how far it should go to ensure this). + @item error @var{errno-code} @samp{ } @var{text} \n The command completed with an error. @var{errno-code} is a symbolic error code (e.g. @code{ENOENT}); if the server doesn't support this @@ -753,72 +1215,182 @@ The command completed successfully. @node Example @section Example -Lines beginning with @samp{c>} are sent by the client; lines beginning -with @samp{s>} are sent by the server; lines beginning with @samp{#} are -not part of the actual exchange. +@c The C:/S: convention is in imitation of RFC1869 (and presumably +@c other RFC's). In other formatting concerns, we might want to think +@c about whether there is an easy way to provide RFC1543 formatting +@c (without negating the advantages of texinfo), and whether we should +@c use RFC822-style BNF (I fear that would be less clear than +@c what we do now, however). Plus what about IETF terminology (SHOULD, +@c MUST, etc.) or ISO terminology (shall, should, or whatever they are)? +Here is an example; lines are prefixed by @samp{C: } to indicate the +client sends them or @samp{S: } to indicate the server sends them. + +The client starts by connecting, sending the root, and completing the +protocol negotiation. In actual practice the lists of valid responses +and requests would be longer. +@c The reason that we artificially shorten the lists is to avoid phony +@c line breaks. Any better solutions? +@c Other than that, this exchange is taken verbatim from the data +@c exchanged by CVS (as of Nov 1996). That is why some of the requests and +@c reponses are not quite what you would pick for pedagogical purposes. + +@example +C: Root /u/cvsroot +C: Valid-responses ok error Checked-in M E +C: valid-requests +S: Valid-requests Root Directory Entry Modified Argument Argumentx ci co +S: ok +C: UseUnchanged +@end example + +The client wants to check out the @code{supermunger} module into a fresh +working directory. Therefore it first expands the @code{supermunger} +module; this step would be omitted if the client was operating on a +directory rather than a module. +@c Why does it send Directory here? The description of expand-modules +@c doesn't really say much of anything about what use, if any, it makes of +@c Directory and similar requests sent previously. + +@example +C: Argument supermunger +C: Directory . +C: /u/cvsroot +C: expand-modules +@end example + +The server replies that the @code{supermunger} module expands to the +directory @code{supermunger} (the simplest case): + +@example +S: Module-expansion supermunger +S: ok +@end example + +The client then proceeds to check out the directory. The fact that it +sends only a single @code{Directory} request which specifies @samp{.} +for the working directory means that there is not already a +@code{supermunger} directory on the client. +@c What is -N doing here? + +@example +C: Argument -N +C: Argument supermunger +C: Directory . +C: /u/cvsroot +C: co +@end example + +The server replies with the requested files. In this example, there is +only one file, @file{mungeall.c}. The @code{Clear-sticky} and +@code{Clear-static-directory} requests are sent by the current +implementation but they have no effect because the default is for those +settings to be clear when a directory is newly created. + +@example +S: Clear-sticky supermunger/ +S: /u/cvsroot/supermunger/ +S: Clear-static-directory supermunger/ +S: /u/cvsroot/supermunger/ +S: E cvs server: Updating supermunger +S: M U supermunger/mungeall.c +S: Created supermunger/ +S: /u/cvsroot/supermunger/mungeall.c +S: /mungeall.c/1.1/// +S: u=rw,g=r,o=r +S: 26 +S: int mein () @{ abort (); @} +S: ok +@end example + +The current client implementation would break the connection here and make a +new connection for the next command. However, the protocol allows it +to keep the connection open and continue, which is what we show here. + +After the user modifies the file and instructs the client to check it +back in. The client sends arguments to specify the log message and file +to check in: + +@example +C: Argument -m +C: Argument Well, you see, it took me hours and hours to find +C: Argumentx this typo and I searched and searched and eventually +C: Argumentx had to ask John for help. +C: Argument mungeall.c +@end example + +It also sends information about the contents of the working directory, +including the new contents of the modified file. Note that the user has +changed into the @file{supermunger} directory before executing this +command; the top level directory is a user-visible concept because the +server should print filenames in @code{M} and @code{E} responses +relative to that directory. +@c We are waving our hands about the order of the requests. "Directory" +@c and "Argument" can be in any order, but this probably isn't specified +@c very well. + +@example +C: Directory . +C: /u/cvsroot/supermunger +C: Entry /mungeall.c/1.1/// +C: Modified mungeall.c +C: u=rw,g=r,o=r +C: 26 +C: int main () @{ abort (); @} +@end example + +And finally, the client issues the checkin command (which makes use of +the data just sent): + +@example +C: ci +@end example + +And the server tells the client that the checkin succeeded: @example -c> Root /rel/cvsfiles -# In actual practice the lists of valid responses and requests would -# be longer -c> Valid-responses Updated Checked-in M ok error -c> valid-requests -s> Valid-requests Root co Modified Entry Repository ci Argument Argumentx -s> ok -# cvs co devo/foo -c> Argument devo/foo -c> co -s> Updated /rel/cvsfiles/devo/foo/foo.c -s> /foo.c/1.4/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993// -s> 26 -s> int mein () @{ abort (); @} -s> Updated /rel/cvsfiles/devo/foo/Makefile -s> /Makefile/1.2/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993// -s> 28 -s> foo: foo.c -s> $(CC) -o foo $< -s> ok -# In actual practice the next part would be a separate connection. -# Here it is shown as part of the same one. -c> Repository /rel/cvsfiles/devo/foo -# foo.c relative to devo/foo just set as Repository. -c> Entry /foo.c/1.4/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993// -c> Entry /Makefile/1.2/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993// -c> Modified foo.c -c> 26 -c> int main () @{ abort (); @} -# cvs ci -m <log message> foo.c -c> Argument -m -c> Argument Well, you see, it took me hours and hours to find this typo and I -c> Argumentx searched and searched and eventually had to ask John for help. -c> Argument foo.c -c> ci -s> Checked-in /rel/cvsfiles/devo/foo/foo.c -s> /foo.c/1.5/ Mon Apr 19 15:54:22 CDT 1993// -s> M Checking in foo.c; -s> M /cygint/rel/cvsfiles/devo/foo/foo.c,v <-- foo.c -s> M new revision: 1.5; previous revision: 1.4 -s> M done -s> ok +S: M Checking in mungeall.c; +S: E /u/cvsroot/supermunger/mungeall.c,v <-- mungeall.c +S: E new revision: 1.2; previous revision: 1.1 +S: E done +S: Mode u=rw,g=r,o=r +S: Checked-in ./ +S: /u/cvsroot/supermunger/mungeall.c +S: /mungeall.c/1.2/// +S: ok @end example @node Requirements @section Required versus optional parts of the protocol -The following are part of every known implementation of the CVS -protocol and it is considered reasonable behavior to completely fail -to work if you are connected with an implementation which attempts to -not support them. Requests: Root, Valid-responses, valid-requests, -Repository, Entry, Modified, Argument, Argumentx, ci, co, update. -Responses: ok, error, Valid-requests, Checked-in, Updated, Merged, -Removed, M, E. - -Failure to support the Directory, UseUnchanged, and Unchanged requests -is deprecated. CVS 1.5 and later have supported these requests and in -the future it will be considered reasonable behavior to completely -fail to work with an implementation which attempts to not support -them. Support for the Repository and Lost requests is deprecated; CVS -clients 1.5 and later will not use them if communicating with a server -which supports Directory and UseUnchanged. +The following are part of every known implementation of the CVS protocol +(except obsolete, pre-1.5, versions of CVS) and it is considered +reasonable behavior to completely fail to work if you are connected with +an implementation which attempts to not support them. Requests: +@code{Root}, @code{Valid-responses}, @code{valid-requests}, +@code{Directory}, @code{Entry}, @code{Modified}, @code{Unchanged}, +@code{Argument}, @code{Argumentx}, @code{ci}, @code{co}, @code{update}. +Responses: @code{ok}, @code{error}, @code{Valid-requests}, +@code{Checked-in}, @code{Updated}, @code{Merged}, @code{Removed}, +@code{M}, @code{E}. + +A server need not implement @code{Repository}, but in order to interoperate +with CVS 1.5 through 1.9 it must claim to implement it (in +@code{Valid-requests}). The client will not actually send the request. + +@node Obsolete +@section Obsolete protocol elements + +This section briefly describes protocol elements which are obsolete. +There is no attempt to document them in full detail. + +There was a @code{Repository} request which was like @code{Directory} +except it only provided @var{repository}, and the local directory was +assumed to be similarly named. + +If the @code{UseUnchanged} request was not sent, there was a @code{Lost} +request which was sent to indicate that a file did not exist in the +working directory, and the meaning of sending @code{Entries} without +@code{Lost} or @code{Modified} was different. All current clients (CVS +1.5 and later) will send @code{UseUnchanged} if it is supported. @bye diff --git a/contrib/cvs/lib/ChangeLog b/contrib/cvs/lib/ChangeLog index 900d6d4..02750d3 100644 --- a/contrib/cvs/lib/ChangeLog +++ b/contrib/cvs/lib/ChangeLog @@ -1,3 +1,134 @@ +Thu Mar 6 17:14:49 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * regex.c: Partial merge with version from emacs 19.34. I brought + over most trivial changes (whitespace and so on). Most of the + changes to portability cruft I did not bring over, on the theory + of sticking to the devil that we know. I did bring over the + change to undef MAX and MIN (this is a better solution to a + problem we had been handling a different way). There were a + variety of changes I probably could/should have brought over, but + elected not to try to understand them and whether they would cause + trouble (printchar -> putchar, changes to output format in + print_partial_compiled_pattern, internationalization, + FREE_STACK_RETURN and friends which would appear to be fixing + memory leaks in error cases, RE_TRANSLATE_TYPE, and others). I + did merge the changes (union fail_stack_elt, PUSH_FAILURE_POINTER, + etc.) to use a union for the failure stack rather than playing + games with pointers and integers (that was my reason for + bothering; the code had been broken on the Alpha). + +Mon Feb 10 18:52:18 1997 Ullrich von Bassewitz <uz@musoftware.com> + + * md5.c: Make the parameter to getu32 const since the function will + only read the values and this will avoid compiler warnings in other + places. + +Mon Feb 10 18:29:04 1997 Ullrich von Bassewitz <uz@musoftware.com> + + * vasprintf.c: Added a #define for systems where a va_list is + defined as an array, not as a pointer. + +Mon Feb 10 09:31:38 1997 Ken Raeburn <raeburn@cygnus.com> + + * md5.c (MD5STEP): Truncate to 32 bits before shifting right. + +Thu Jan 30 11:35:26 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * regex.h: Don't prototype re_comp and re_exec. + +Tue Jan 28 17:45:46 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * md5.c, md5.h: Changes so these work without having an integer + type which is exactly 32 bits. Modeled after changes by Tatu Ylonen + <ylo@cs.hut.fi> as part of SSH but rewritten. + +Wed Jan 8 14:50:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in, getopt.h, sighandle.c, system.h: Remove CVSid; we + decided to get rid of these some time ago. + +Thu Jan 2 13:30:56 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in, argmatch.c, fnmatch.c, fnmatch.h, getline.c, + getopt.c, getopt.h, getopt1.c, getwd.c, hostname.c, mkdir.c, + regex.c, regex.h, rename.c, sighandle.c, strdup.c, strerror.c, + stripslash.c, system.h, vasprintf.c, wait.h, xgetwd.c, yesno.c: + Remove "675" paragraph; see ../ChangeLog for rationale. + +Sun Nov 24 13:34:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * getdate.y (Convert): Change last acceptable year from 1999 to + 2038. + * getdate.c: Regenerated using byacc 1.9. + +Tue Nov 19 17:11:17 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (OBJECTS): Remove strippath.o; we don't use + strip_path anymore. + (SOURCES): Remove strippath.c. + * strippath.c: Removed. + * build_lib.com: Remove strippath.c. + +Wed Oct 2 10:43:35 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * getdate.y: removed CVSid variable + + * getdate.c: regenerated (using byacc 1.9) + +Wed Sep 25 10:25:00 1996 Larry Jones <larry.jones@sdrc.com> + + * vasprintf.c: Fix type clashes in calls to strtoul. + +Wed Sep 11 15:55:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * build_lib.com: Add valloc.c. + +Tue Sep 10 23:04:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (DISTFILES): Add build_lib.com. + +Fri Aug 16 16:01:57 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * Makefile.in (installdirs): new (empty) target + +Mon Aug 12 11:03:43 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * system.h: Don't use #elif. It is said to cause problems with + one of the HP compilers on HPUX 9.01. + +Sun Jul 7 23:25:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * memmove.c: Removed. The memove function was used by a very old + version of the CVS server for nefarious purposes and it has been + long gone. + * Makefile.in (SOURCES): Remove memmove.c. + +Thu Jun 6 15:12:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * vasprintf.c: If STDC_HEADERS, include stdlib.h rather than + declaring its functions ourself. + +Wed Jun 05 10:14:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * system.h: If ERRNO_H_MISSING is defined, don't include errno.h. + +Wed Jun 05 10:14:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * regex.c: Don't define MAX and MIN if already defined. + +Sun May 12 09:40:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * getdate.y: Replace alloca.h include with a comment explaining + why we avoid alloca and the consequences of that. + * getdate.c: Regenerated. + +Wed May 8 09:31:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * getdate.c: Regenerate with the version of byacc in Red Hat 3.0.3 + (which I believe is byacc 1.9). byacc, unlike bison, does not + require alloca in the generated parser. + Thu Apr 25 18:26:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> * getdate.y (get_date): Set Start from nowtime, not now->time, diff --git a/contrib/cvs/lib/Makefile.in b/contrib/cvs/lib/Makefile.in index 9fb93f3..9645ae9 100644 --- a/contrib/cvs/lib/Makefile.in +++ b/contrib/cvs/lib/Makefile.in @@ -12,11 +12,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# $CVSid: @(#)Makefile.in 1.21 94/09/24 $ srcdir = @srcdir@ top_srcdir = @top_srcdir@ cvs_srcdir = @top_srcdir@/src @@ -34,13 +29,12 @@ SOURCES = \ ftruncate.c \ getdate.c \ getdate.y \ - getline.c \ + getline.c \ getopt.c \ getopt1.c \ getwd.c \ hostname.c \ md5.c \ - memmove.c \ mkdir.c \ regex.c \ rename.c \ @@ -49,7 +43,6 @@ SOURCES = \ strdup.c \ strstr.c \ strerror.c \ - strippath.c \ stripslash.c \ strtoul.c \ valloc.c \ @@ -69,7 +62,6 @@ OBJECTS = \ md5.o \ savecwd.o \ sighandle.o \ - strippath.o \ stripslash.o \ xgetwd.o \ yesno.o \ @@ -77,7 +69,7 @@ OBJECTS = \ DISTFILES = \ .cvsignore ChangeLog ChangeLog.fsf Makefile.in \ - ${SOURCES} ${HEADERS} + ${SOURCES} ${HEADERS} build_lib.com DEFS = @DEFS@ RANLIB = @RANLIB@ @@ -98,6 +90,9 @@ all: libcvs.a install: all .PHONY: install +installdirs: +.PHONY: installdirs + tags: $(DISTFILES) ctags `for i in $(DISTFILES); do echo $(srcdir)/$$i; done` diff --git a/contrib/cvs/lib/argmatch.c b/contrib/cvs/lib/argmatch.c index cc360ee..90b44c6 100644 --- a/contrib/cvs/lib/argmatch.c +++ b/contrib/cvs/lib/argmatch.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ /* Written by David MacKenzie <djm@ai.mit.edu> */ diff --git a/contrib/cvs/lib/fnmatch.c b/contrib/cvs/lib/fnmatch.c index 9cb847e..cf0f124 100644 --- a/contrib/cvs/lib/fnmatch.c +++ b/contrib/cvs/lib/fnmatch.c @@ -9,12 +9,7 @@ License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with the GNU C Library; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 675 Mass Ave, -Cambridge, MA 02139, USA. */ +Library General Public License for more details. */ /* Modified slightly by Brian Berliner <berliner@sun.com> and Jim Blandy <jimb@cyclic.com> for CVS use */ diff --git a/contrib/cvs/lib/fnmatch.h b/contrib/cvs/lib/fnmatch.h index a1e4f87..b157347 100644 --- a/contrib/cvs/lib/fnmatch.h +++ b/contrib/cvs/lib/fnmatch.h @@ -9,12 +9,7 @@ License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with the GNU C Library; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 675 Mass Ave, -Cambridge, MA 02139, USA. */ +Library General Public License for more details. */ #ifndef _FNMATCH_H diff --git a/contrib/cvs/lib/getdate.y b/contrib/cvs/lib/getdate.y index 5787142..8ed565c 100644 --- a/contrib/cvs/lib/getdate.y +++ b/contrib/cvs/lib/getdate.y @@ -96,9 +96,18 @@ struct timeb { #include <stdlib.h> #endif -#if defined (HAVE_ALLOCA_H) -#include <alloca.h> -#endif +/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS + releases): + + We don't want to mess with all the portability hassles of alloca. + In particular, most (all?) versions of bison will use alloca in + their parser. If bison works on your system (e.g. it should work + with gcc), then go ahead and use it, but the more general solution + is to use byacc instead of bison, which should generate a portable + parser. I played with adding "#define alloca dont_use_alloca", to + give an error if the parser generator uses alloca (and thus detect + unportable getdate.c's), but that seems to cause as many problems + as it solves. */ extern struct tm *gmtime(); extern struct tm *localtime(); @@ -107,10 +116,6 @@ extern struct tm *localtime(); #define yylex getdate_yylex #define yyerror getdate_yyerror -#if !defined(lint) && !defined(SABER) -static char RCS[] = "$CVSid: @(#)getdate.y 1.11 94/09/21 $"; -#endif /* !defined(lint) && !defined(SABER) */ - static int yylex (); static int yyerror (); @@ -635,7 +640,9 @@ Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) Year += 1900; DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 29 : 28; - if (Year < EPOCH || Year > 1999 + /* Checking for 2038 bogusly assumes that time_t is 32 bits. But + I'm too lazy to try to check for time_t overflow in another way. */ + if (Year < EPOCH || Year > 2038 || Month < 1 || Month > 12 /* Lint fluff: "conversion from long may lose accuracy" */ || Day < 1 || Day > DaysInMonth[(int)--Month]) diff --git a/contrib/cvs/lib/getline.c b/contrib/cvs/lib/getline.c index a7ab97b..5f4fba6 100644 --- a/contrib/cvs/lib/getline.c +++ b/contrib/cvs/lib/getline.c @@ -10,11 +10,7 @@ License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +General Public License for more details. */ /* Written by Jan Brittenson, bson@gnu.ai.mit.edu. */ diff --git a/contrib/cvs/lib/getopt.c b/contrib/cvs/lib/getopt.c index 137e66b..b5caccd 100644 --- a/contrib/cvs/lib/getopt.c +++ b/contrib/cvs/lib/getopt.c @@ -14,11 +14,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ /* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. Ditto for AIX 3.2 and <stdlib.h>. */ diff --git a/contrib/cvs/lib/getopt.h b/contrib/cvs/lib/getopt.h index f644aa1..c872f414 100644 --- a/contrib/cvs/lib/getopt.h +++ b/contrib/cvs/lib/getopt.h @@ -9,13 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* $CVSid: @(#)getopt.h 1.7 94/09/21 $ */ + GNU General Public License for more details. */ #ifndef _GETOPT_H #define _GETOPT_H 1 diff --git a/contrib/cvs/lib/getopt1.c b/contrib/cvs/lib/getopt1.c index f784b57..a4f1976 100644 --- a/contrib/cvs/lib/getopt1.c +++ b/contrib/cvs/lib/getopt1.c @@ -10,11 +10,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H #if defined (emacs) || defined (CONFIG_BROKETS) diff --git a/contrib/cvs/lib/getwd.c b/contrib/cvs/lib/getwd.c index 573a788..5707dcb 100644 --- a/contrib/cvs/lib/getwd.c +++ b/contrib/cvs/lib/getwd.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ /* Some systems which include both getwd() and getcwd() have an implementation of getwd() which is much faster than getcwd(). As a result, we use the @@ -31,5 +27,7 @@ char * getwd (pathname) char *pathname; { + char *getcwd(); + return (getcwd(pathname, PATH_MAX)); } diff --git a/contrib/cvs/lib/hostname.c b/contrib/cvs/lib/hostname.c index 34be15e..7fde534 100644 --- a/contrib/cvs/lib/hostname.c +++ b/contrib/cvs/lib/hostname.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/cvs/lib/md5.c b/contrib/cvs/lib/md5.c index 4ad99cd..1003a40 100644 --- a/contrib/cvs/lib/md5.c +++ b/contrib/cvs/lib/md5.c @@ -15,7 +15,17 @@ * will fill a supplied 16-byte array with the digest. */ +/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to + not require an integer type which is exactly 32 bits. This work + draws on the changes for the same purpose by Tatu Ylonen + <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use + that code, there is no copyright issue. I hereby disclaim + copyright in any changes I have made; this code remains in the + public domain. */ + +#ifdef HAVE_CONFIG_H #include "config.h" +#endif #if HAVE_STRING_H || STDC_HEADERS #include <string.h> /* for memcpy() */ @@ -32,25 +42,30 @@ #include "md5.h" -void byteReverse PROTO ((unsigned char *buf, unsigned longs)); +/* Little-endian byte-swapping routines. Note that these do not + depend on the size of datatypes such as uint32, nor do they require + us to detect the endianness of the machine we are running on. It + is possible they should be macros for speed, but I would be + surprised if they were a performance bottleneck for MD5. */ -#ifndef ASM_MD5 -/* - * Note: this code is harmless on little-endian machines. - */ -void byteReverse (buf, longs) - unsigned char *buf; - unsigned longs; +static uint32 +getu32 (addr) + const unsigned char *addr; { - uint32 t; - do { - t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | - ((unsigned)buf[1]<<8 | buf[0]); - *(uint32 *)buf = t; - buf += 4; - } while (--longs); + return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) + | addr[1]) << 8 | addr[0]; +} + +static void +putu32 (data, addr) + uint32 data; + unsigned char *addr; +{ + addr[0] = (unsigned char)data; + addr[1] = (unsigned char)(data >> 8); + addr[2] = (unsigned char)(data >> 16); + addr[3] = (unsigned char)(data >> 24); } -#endif /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious @@ -84,7 +99,7 @@ MD5Update(ctx, buf, len) /* Update bitcount */ t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) + if ((ctx->bits[0] = (t + ((uint32)len << 3)) & 0xffffffff) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; @@ -93,7 +108,7 @@ MD5Update(ctx, buf, len) /* Handle any leading odd-sized chunks */ if ( t ) { - unsigned char *p = (unsigned char *)ctx->in + t; + unsigned char *p = ctx->in + t; t = 64-t; if (len < t) { @@ -101,8 +116,7 @@ MD5Update(ctx, buf, len) return; } memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); + MD5Transform(ctx->buf, ctx->in); buf += t; len -= t; } @@ -111,8 +125,7 @@ MD5Update(ctx, buf, len) while (len >= 64) { memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); + MD5Transform(ctx->buf, ctx->in); buf += 64; len -= 64; } @@ -149,8 +162,7 @@ MD5Final(digest, ctx) if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); + MD5Transform(ctx->buf, ctx->in); /* Now fill the next block with 56 bytes */ memset(ctx->in, 0, 56); @@ -158,15 +170,16 @@ MD5Final(digest, ctx) /* Pad block to 56 bytes */ memset(p, 0, count-8); } - byteReverse(ctx->in, 14); /* Append length in bits and transform */ - ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; - ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; - - MD5Transform(ctx->buf, (uint32 *)ctx->in); - byteReverse((unsigned char *)ctx->buf, 4); - memcpy(digest, ctx->buf, 16); + putu32(ctx->bits[0], ctx->in + 56); + putu32(ctx->bits[1], ctx->in + 60); + + MD5Transform(ctx->buf, ctx->in); + putu32(ctx->buf[0], digest); + putu32(ctx->buf[1], digest + 4); + putu32(ctx->buf[2], digest + 8); + putu32(ctx->buf[3], digest + 12); memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ } @@ -182,7 +195,7 @@ MD5Final(digest, ctx) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to @@ -190,11 +203,16 @@ MD5Final(digest, ctx) * the data and converts bytes into longwords for this routine. */ void -MD5Transform(buf, in) +MD5Transform(buf, inraw) uint32 buf[4]; - uint32 const in[16]; + const unsigned char inraw[64]; { register uint32 a, b, c, d; + uint32 in[16]; + int i; + + for (i = 0; i < 16; ++i) + in[i] = getu32 (inraw + 4 * i); a = buf[0]; b = buf[1]; @@ -275,3 +293,37 @@ MD5Transform(buf, in) buf[3] += d; } #endif + +#ifdef TEST +/* Simple test program. Can use it to manually run the tests from + RFC1321 for example. */ +#include <stdio.h> + +int +main (int argc, char **argv) +{ + struct MD5Context context; + unsigned char checksum[16]; + int i; + int j; + + if (argc < 2) + { + fprintf (stderr, "usage: %s string-to-hash\n", argv[0]); + exit (1); + } + for (j = 1; j < argc; ++j) + { + printf ("MD5 (\"%s\") = ", argv[j]); + MD5Init (&context); + MD5Update (&context, argv[j], strlen (argv[j])); + MD5Final (checksum, &context); + for (i = 0; i < 16; i++) + { + printf ("%02x", (unsigned int) checksum[i]); + } + printf ("\n"); + } + return 0; +} +#endif /* TEST */ diff --git a/contrib/cvs/lib/md5.h b/contrib/cvs/lib/md5.h index bfe79cc..65bac1f 100644 --- a/contrib/cvs/lib/md5.h +++ b/contrib/cvs/lib/md5.h @@ -1,16 +1,14 @@ +/* See md5.c for explanation and copyright information. */ + #ifndef MD5_H #define MD5_H -#if SIZEOF_LONG == 4 +/* Unlike previous versions of this code, uint32 need not be exactly + 32 bits, merely 32 bits or more. Choosing a data type which is 32 + bits instead of 64 is not important; speed is considerably more + important. ANSI guarantees that "unsigned long" will be big enough, + and always using it seems to have few disadvantages. */ typedef unsigned long uint32; -#else -#if SIZEOF_INT == 4 -typedef unsigned int uint32; -#else -Congratulations! You get to rewrite this code so that it does not require -a 32-bit integer type! (Or maybe you just need to reconfigure.) -#endif -#endif struct MD5Context { uint32 buf[4]; @@ -21,7 +19,7 @@ struct MD5Context { void MD5Init PROTO((struct MD5Context *context)); void MD5Update PROTO((struct MD5Context *context, unsigned char const *buf, unsigned len)); void MD5Final PROTO((unsigned char digest[16], struct MD5Context *context)); -void MD5Transform PROTO((uint32 buf[4], uint32 const in[16])); +void MD5Transform PROTO((uint32 buf[4], const unsigned char in[64])); /* * This is needed to make RSAREF happy on some MS-DOS compilers. diff --git a/contrib/cvs/lib/mkdir.c b/contrib/cvs/lib/mkdir.c index 89ed4b6..babe4ac 100644 --- a/contrib/cvs/lib/mkdir.c +++ b/contrib/cvs/lib/mkdir.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/cvs/lib/regex.c b/contrib/cvs/lib/regex.c index 03fc721..ddeca2a 100644 --- a/contrib/cvs/lib/regex.c +++ b/contrib/cvs/lib/regex.c @@ -3,7 +3,7 @@ (Implements POSIX draft P10003.2/D11.2, except for internationalization features.) - Copyright (C) 1993 Free Software Foundation, Inc. + Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,11 +13,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ /* Trying to define this in the makefile would get hairy, unless we can more gracefully do it for NT, OS/2, unix, etc. */ @@ -79,7 +75,7 @@ char *realloc (); /* This must be nonzero for the wordchar and notwordchar pattern commands in re_match_2. */ -#ifndef Sword +#ifndef Sword #define Sword 1 #endif @@ -177,8 +173,8 @@ init_syntax_once () use `alloca' instead of `malloc'. This is because using malloc in re_search* or re_match* could cause memory leaks when C-g is used in Emacs; also, malloc is slower and causes storage fragmentation. On - the other hand, malloc is more portable, and easier to debug. - + the other hand, malloc is more portable, and easier to debug. + Because we sometimes use alloca, some routines have to be macros, not functions -- `alloca'-allocated space disappears at the end of the function it is called in. */ @@ -234,6 +230,8 @@ char *alloca (); #define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) +#undef MAX +#undef MIN #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -306,9 +304,9 @@ typedef enum /* Analogously, for end of buffer/string. */ endbuf, - + /* Followed by two byte relative address to which to jump. */ - jump, + jump, /* Same as jump, but marks the end of an alternative. */ jump_past_alt, @@ -316,11 +314,11 @@ typedef enum /* Followed by two-byte relative address of place to resume at in case of failure. */ on_failure_jump, - + /* Like on_failure_jump, but pushes a placeholder instead of the current string position when executed. */ on_failure_keep_string_jump, - + /* Throw away latest failure point and then jump to following two-byte relative address. */ pop_failure_jump, @@ -416,7 +414,7 @@ extract_number (dest, source) int *dest; unsigned char *source; { - int temp = SIGN_EXTEND_CHAR (*(source + 1)); + int temp = SIGN_EXTEND_CHAR (*(source + 1)); *dest = *source & 0377; *dest += temp << 8; } @@ -442,7 +440,7 @@ static void extract_number_and_incr (destination, source) int *destination; unsigned char **source; -{ +{ extract_number (destination, *source); *source += 2; } @@ -491,8 +489,8 @@ print_fastmap (fastmap) char *fastmap; { unsigned was_a_range = 0; - unsigned i = 0; - + unsigned i = 0; + while (i < (1 << BYTEWIDTH)) { if (fastmap[i++]) @@ -511,7 +509,7 @@ print_fastmap (fastmap) } } } - putchar ('\n'); + putchar ('\n'); } @@ -532,7 +530,7 @@ print_partial_compiled_pattern (start, end) printf ("(null)\n"); return; } - + /* Loop over pattern commands. */ while (p < pend) { @@ -643,24 +641,24 @@ print_partial_compiled_pattern (start, end) printf ("/jump/0/%d", mcnt); break; - case succeed_n: + case succeed_n: extract_number_and_incr (&mcnt, &p); extract_number_and_incr (&mcnt2, &p); printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2); break; - - case jump_n: + + case jump_n: extract_number_and_incr (&mcnt, &p); extract_number_and_incr (&mcnt2, &p); printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2); break; - - case set_number_at: + + case set_number_at: extract_number_and_incr (&mcnt, &p); extract_number_and_incr (&mcnt2, &p); printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2); break; - + case wordbound: printf ("/wordbound"); break; @@ -672,10 +670,10 @@ print_partial_compiled_pattern (start, end) case wordbeg: printf ("/wordbeg"); break; - + case wordend: printf ("/wordend"); - + #ifdef emacs case before_dot: printf ("/before_dot"); @@ -694,7 +692,7 @@ print_partial_compiled_pattern (start, end) mcnt = *p++; printf ("/%d", mcnt); break; - + case notsyntaxspec: printf ("/notsyntaxspec"); mcnt = *p++; @@ -705,7 +703,7 @@ print_partial_compiled_pattern (start, end) case wordchar: printf ("/wordchar"); break; - + case notwordchar: printf ("/notwordchar"); break; @@ -762,7 +760,7 @@ print_double_string (where, string1, size1, string2, size2) int size2; { unsigned this_char; - + if (where == NULL) printf ("(null)"); else @@ -772,7 +770,7 @@ print_double_string (where, string1, size1, string2, size2) for (this_char = where - string1; this_char < size1; this_char++) printchar (string1[this_char]); - where = string2; + where = string2; } for (this_char = where - string2; this_char < size2; this_char++) @@ -813,7 +811,7 @@ re_set_syntax (syntax) reg_syntax_t syntax; { reg_syntax_t ret = re_syntax_options; - + re_syntax_options = syntax; return ret; } @@ -989,7 +987,7 @@ typedef struct pattern_offset_t begalt_offset; pattern_offset_t fixup_alt_jump; pattern_offset_t inner_group_offset; - pattern_offset_t laststart_offset; + pattern_offset_t laststart_offset; regnum_t regnum; } compile_stack_elt_t; @@ -1032,7 +1030,7 @@ typedef struct PATFETCH (c); \ } \ } \ - } + } #define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ @@ -1058,7 +1056,7 @@ typedef struct `fastmap_accurate' is zero; `re_nsub' is the number of subexpressions in PATTERN; `not_bol' and `not_eol' are zero; - + The `fastmap' and `newline_anchor' fields are neither examined nor set. */ @@ -1073,20 +1071,20 @@ regex_compile (pattern, size, syntax, bufp) `char *' (i.e., signed), we declare these variables as unsigned, so they can be reliably used as array indices. */ register unsigned char c, c1; - - /* A random tempory spot in PATTERN. */ + + /* A random temporary spot in PATTERN. */ const char *p1; /* Points to the end of the buffer, where we should append. */ register unsigned char *b; - + /* Keeps track of unclosed groups. */ compile_stack_type compile_stack; /* Points to the current (ending) position in the pattern. */ const char *p = pattern; const char *pend = pattern + size; - + /* How to translate the characters in the pattern. */ char *translate = bufp->translate; @@ -1107,7 +1105,7 @@ regex_compile (pattern, size, syntax, bufp) /* Place in the uncompiled pattern (i.e., the {) to which to go back if the interval is invalid. */ const char *beg_interval; - + /* Address of the place where a forward jump should go to the end of the containing expression. Each alternative of an `or' -- except the last -- ends with a forward jump of this sort. */ @@ -1123,7 +1121,7 @@ regex_compile (pattern, size, syntax, bufp) if (debug) { unsigned debug_count; - + for (debug_count = 0; debug_count < size; debug_count++) printchar (pattern[debug_count]); putchar ('\n'); @@ -1147,9 +1145,9 @@ regex_compile (pattern, size, syntax, bufp) printer (for debugging) will think there's no pattern. We reset it at the end. */ bufp->used = 0; - + /* Always count groups, whether or not bufp->no_sub is set. */ - bufp->re_nsub = 0; + bufp->re_nsub = 0; #if !defined (emacs) && !defined (SYNTAX_TABLE) /* Initialize the syntax table. */ @@ -1200,7 +1198,7 @@ regex_compile (pattern, size, syntax, bufp) case '$': { if ( /* If at end of pattern, it's an operator. */ - p == pend + p == pend /* If context independent, it's an operator. */ || syntax & RE_CONTEXT_INDEP_ANCHORS /* Otherwise, depends on what's next. */ @@ -1231,7 +1229,7 @@ regex_compile (pattern, size, syntax, bufp) { /* Are we optimizing this jump? */ boolean keep_string_p = false; - + /* 1 means zero (many) matches is allowed. */ char zero_times_ok = 0, many_times_ok = 0; @@ -1279,7 +1277,7 @@ regex_compile (pattern, size, syntax, bufp) /* Star, etc. applied to an empty pattern is equivalent to an empty pattern. */ - if (!laststart) + if (!laststart) break; /* Now we know whether or not zero matches is allowed @@ -1288,7 +1286,7 @@ regex_compile (pattern, size, syntax, bufp) { /* More than one repetition is allowed, so put in at the end a backward relative jump from `b' to before the next jump we're going to put in below (which jumps from - laststart to after this jump). + laststart to after this jump). But if we are at the `*' in the exact sequence `.*\n', insert an unconditional jump backwards to the ., @@ -1415,8 +1413,8 @@ regex_compile (pattern, size, syntax, bufp) was a character: if this is a hyphen not at the beginning or the end of a list, then it's the range operator. */ - if (c == '-' - && !(p - 2 >= pattern && p[-2] == '[') + if (c == '-' + && !(p - 2 >= pattern && p[-2] == '[') && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^') && *p != ']') { @@ -1431,7 +1429,7 @@ regex_compile (pattern, size, syntax, bufp) /* Move past the `-'. */ PATFETCH (c1); - + ret = compile_range (&p, pend, translate, syntax, b); if (ret != REG_NOERROR) return ret; } @@ -1460,7 +1458,7 @@ regex_compile (pattern, size, syntax, bufp) str[c1] = '\0'; /* If isn't a word bracketed by `[:' and:`]': - undo the ending character, the letters, and leave + undo the ending character, the letters, and leave the leading `:' and `[' (but set bits for them). */ if (c == ':' && *p == ']') { @@ -1477,12 +1475,12 @@ regex_compile (pattern, size, syntax, bufp) boolean is_space = STREQ (str, "space"); boolean is_upper = STREQ (str, "upper"); boolean is_xdigit = STREQ (str, "xdigit"); - + if (!IS_CHAR_CLASS (str)) return REG_ECTYPE; /* Throw away the ] at the end of the character class. */ - PATFETCH (c); + PATFETCH (c); if (p == pend) return REG_EBRACK; @@ -1507,7 +1505,7 @@ regex_compile (pattern, size, syntax, bufp) else { c1++; - while (c1--) + while (c1--) PATUNFETCH; SET_LIST_BIT ('['); SET_LIST_BIT (':'); @@ -1523,8 +1521,8 @@ regex_compile (pattern, size, syntax, bufp) /* Discard any (non)matching list bytes that are all 0 at the end of the map. Decrease the map-length byte too. */ - while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) - b[-1]--; + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; b += b[-1]; } break; @@ -1584,7 +1582,7 @@ regex_compile (pattern, size, syntax, bufp) regnum++; if (COMPILE_STACK_FULL) - { + { RETALLOC (compile_stack.stack, compile_stack.size << 1, compile_stack_elt_t); if (compile_stack.stack == NULL) return REG_ESPACE; @@ -1597,7 +1595,7 @@ regex_compile (pattern, size, syntax, bufp) whole pattern moves because of realloc, they will still be valid. */ COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; - COMPILE_STACK_TOP.fixup_alt_jump + COMPILE_STACK_TOP.fixup_alt_jump = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; COMPILE_STACK_TOP.regnum = regnum; @@ -1611,7 +1609,7 @@ regex_compile (pattern, size, syntax, bufp) COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; BUF_PUSH_3 (start_memory, regnum, 0); } - + compile_stack.avail++; fixup_alt_jump = 0; @@ -1640,7 +1638,7 @@ regex_compile (pattern, size, syntax, bufp) `pop_failure_jump' to pop. See comments at `push_dummy_failure' in `re_match_2'. */ BUF_PUSH (push_dummy_failure); - + /* We allocated space for this jump when we assigned to `fixup_alt_jump', in the `handle_alt' case below. */ STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); @@ -1662,11 +1660,11 @@ regex_compile (pattern, size, syntax, bufp) as in `(ab)c(de)' -- the second group is #2. */ regnum_t this_group_regnum; - compile_stack.avail--; + compile_stack.avail--; begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; fixup_alt_jump = COMPILE_STACK_TOP.fixup_alt_jump - ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 + ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 : 0; laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; this_group_regnum = COMPILE_STACK_TOP.regnum; @@ -1681,7 +1679,7 @@ regex_compile (pattern, size, syntax, bufp) { unsigned char *inner_group_loc = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; - + *inner_group_loc = regnum - this_group_regnum; BUF_PUSH_3 (stop_memory, this_group_regnum, regnum - this_group_regnum); @@ -1710,10 +1708,10 @@ regex_compile (pattern, size, syntax, bufp) jump (put in below, which in turn will jump to the next (if any) alternative's such jump, etc.). The last such jump jumps to the correct final destination. A picture: - _____ _____ - | | | | - | v | v - a | b | c + _____ _____ + | | | | + | v | v + a | b | c If we are at `b', then fixup_alt_jump right now points to a three-byte space after `a'. We'll put in the jump, set @@ -1735,10 +1733,10 @@ regex_compile (pattern, size, syntax, bufp) break; - case '{': + case '{': /* If \{ is a literal. */ if (!(syntax & RE_INTERVALS) - /* If we're at `\{' and it's not the open-interval + /* If we're at `\{' and it's not the open-interval operator. */ || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) || (p - 2 == pattern && p == pend)) @@ -1777,11 +1775,11 @@ regex_compile (pattern, size, syntax, bufp) { if (syntax & RE_NO_BK_BRACES) goto unfetch_interval; - else + else return REG_BADBR; } - if (!(syntax & RE_NO_BK_BRACES)) + if (!(syntax & RE_NO_BK_BRACES)) { if (c != '\\') return REG_EBRACE; @@ -1792,7 +1790,7 @@ regex_compile (pattern, size, syntax, bufp) { if (syntax & RE_NO_BK_BRACES) goto unfetch_interval; - else + else return REG_BADBR; } @@ -1823,12 +1821,12 @@ regex_compile (pattern, size, syntax, bufp) we're all done, the pattern will look like: set_number_at <jump count> <upper bound> set_number_at <succeed_n count> <lower bound> - succeed_n <after jump addr> <succed_n count> + succeed_n <after jump addr> <succeed_n count> <body of loop> jump_n <succeed_n addr> <jump count> (The upper bound and `jump_n' are omitted if `upper_bound' is 1, though.) */ - else + else { /* If the upper bound is > 1, we need to insert more at the end of the loop. */ unsigned nbytes = 10 + (upper_bound > 1) * 10; @@ -1845,7 +1843,7 @@ regex_compile (pattern, size, syntax, bufp) lower_bound); b += 5; - /* Code to initialize the lower bound. Insert + /* Code to initialize the lower bound. Insert before the `succeed_n'. The `5' is the last two bytes of this `set_number_at', plus 3 bytes of the following `succeed_n'. */ @@ -1856,7 +1854,7 @@ regex_compile (pattern, size, syntax, bufp) { /* More than one repetition is allowed, so append a backward jump to the `succeed_n' that starts this interval. - + When we've reached this during matching, we'll have matched the interval once, so jump back only `upper_bound - 1' times. */ @@ -1874,7 +1872,7 @@ regex_compile (pattern, size, syntax, bufp) so everything is getting moved up by 5. Conclusion: (b - 2) - (laststart + 3) + 5, i.e., b - laststart. - + We insert this at the beginning of the loop so that if we fail during matching, we'll reinitialize the bounds. */ @@ -1895,7 +1893,7 @@ regex_compile (pattern, size, syntax, bufp) beg_interval = NULL; /* normal_char and normal_backslash need `c'. */ - PATFETCH (c); + PATFETCH (c); if (!(syntax & RE_NO_BK_BRACES)) { @@ -1911,7 +1909,7 @@ regex_compile (pattern, size, syntax, bufp) BUF_PUSH (at_dot); break; - case 's': + case 's': laststart = b; PATFETCH (c); BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); @@ -2002,11 +2000,11 @@ regex_compile (pattern, size, syntax, bufp) /* Expects the character in `c'. */ normal_char: /* If no exactn currently being built. */ - if (!pending_exact + if (!pending_exact /* If last exactn not at current position. */ || pending_exact + *pending_exact + 1 != b - + /* We have only one byte following the exactn for the count. */ || *pending_exact == (1 << BYTEWIDTH) - 1 @@ -2021,26 +2019,26 @@ regex_compile (pattern, size, syntax, bufp) : (p[0] == '\\' && p[1] == '{')))) { /* Start building a new exactn. */ - + laststart = b; BUF_PUSH_2 (exactn, 0); pending_exact = b - 1; } - + BUF_PUSH (c); (*pending_exact)++; break; } /* switch (c) */ } /* while p != pend */ - + /* Through the pattern now. */ - + if (fixup_alt_jump) STORE_JUMP (jump_past_alt, fixup_alt_jump, b); - if (!COMPILE_STACK_EMPTY) + if (!COMPILE_STACK_EMPTY) return REG_EPAREN; free (compile_stack.stack); @@ -2096,14 +2094,14 @@ insert_op1 (op, loc, arg, end) re_opcode_t op; unsigned char *loc; int arg; - unsigned char *end; + unsigned char *end; { register unsigned char *pfrom = end; register unsigned char *pto = end + 3; while (pfrom != loc) *--pto = *--pfrom; - + store_op1 (op, loc, arg); } @@ -2115,14 +2113,14 @@ insert_op2 (op, loc, arg1, arg2, end) re_opcode_t op; unsigned char *loc; int arg1, arg2; - unsigned char *end; + unsigned char *end; { register unsigned char *pfrom = end; register unsigned char *pto = end + 5; while (pfrom != loc) *--pto = *--pfrom; - + store_op2 (op, loc, arg1, arg2); } @@ -2138,7 +2136,7 @@ at_begline_loc_p (pattern, p, syntax) { const char *prev = p - 2; boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; - + return /* After a subexpression? */ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) @@ -2263,7 +2261,13 @@ compile_range (p_ptr, pend, translate, syntax, b) change it ourselves. */ int re_max_failures = 2000; -typedef const unsigned char *fail_stack_elt_t; +union fail_stack_elt +{ + unsigned char *pointer; + int integer; +}; + +typedef union fail_stack_elt fail_stack_elt_t; typedef struct { @@ -2318,26 +2322,41 @@ typedef struct Return 1 if was able to do so and 0 if ran out of memory allocating space to do so. */ -#define PUSH_PATTERN_OP(pattern_op, fail_stack) \ +#define PUSH_PATTERN_OP(POINTER, FAIL_STACK) \ ((FAIL_STACK_FULL () \ - && !DOUBLE_FAIL_STACK (fail_stack)) \ - ? 0 \ - : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \ - 1)) + && !DOUBLE_FAIL_STACK (FAIL_STACK)) \ + ? 0 \ + : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER, \ + 1)) + +/* Push a pointer value onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_POINTER(item) \ + fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item) -/* This pushes an item onto the failure stack. Must be a four-byte - value. Assumes the variable `fail_stack'. Probably should only +/* This pushes an integer-valued item onto the failure stack. + Assumes the variable `fail_stack'. Probably should only be called from within `PUSH_FAILURE_POINT'. */ -#define PUSH_FAILURE_ITEM(item) \ - fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item +#define PUSH_FAILURE_INT(item) \ + fail_stack.stack[fail_stack.avail++].integer = (item) -/* The complement operation. Assumes `fail_stack' is nonempty. */ -#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail] +/* Push a fail_stack_elt_t value onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_ELT(item) \ + fail_stack.stack[fail_stack.avail++] = (item) + +/* These three POP... operations complement the three PUSH... operations. + All assume that `fail_stack' is nonempty. */ +#define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer +#define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer +#define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail] /* Used to omit pushing failure point id's when we're not debugging. */ #ifdef DEBUG -#define DEBUG_PUSH PUSH_FAILURE_ITEM -#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM () +#define DEBUG_PUSH PUSH_FAILURE_INT +#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT () #else #define DEBUG_PUSH(item) #define DEBUG_POP(item_addr) @@ -2345,12 +2364,12 @@ typedef struct /* Push the information about the state we will need - if we ever fail back to it. - + if we ever fail back to it. + Requires variables fail_stack, regstart, regend, reg_info, and num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be declared. - + Does `return FAILURE_CODE' if runs out of memory. */ #define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ @@ -2372,7 +2391,7 @@ typedef struct /* Ensure we have enough space allocated for what we will push. */ \ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ { \ - if (!DOUBLE_FAIL_STACK (fail_stack)) \ + if (!DOUBLE_FAIL_STACK (fail_stack)) \ return failure_code; \ \ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ @@ -2383,45 +2402,46 @@ typedef struct /* Push the info, starting with the registers. */ \ DEBUG_PRINT1 ("\n"); \ \ - for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ - this_reg++) \ - { \ - DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ - DEBUG_STATEMENT (num_regs_pushed++); \ + if (1) \ + for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ + this_reg++) \ + { \ + DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ + DEBUG_STATEMENT (num_regs_pushed++); \ \ - DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ - PUSH_FAILURE_ITEM (regstart[this_reg]); \ - \ - DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ - PUSH_FAILURE_ITEM (regend[this_reg]); \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + PUSH_FAILURE_POINTER (regstart[this_reg]); \ \ - DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ - DEBUG_PRINT2 (" match_null=%d", \ - REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ - DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ - DEBUG_PRINT2 (" matched_something=%d", \ - MATCHED_SOMETHING (reg_info[this_reg])); \ - DEBUG_PRINT2 (" ever_matched=%d", \ - EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ - DEBUG_PRINT1 ("\n"); \ - PUSH_FAILURE_ITEM (reg_info[this_reg].word); \ - } \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + PUSH_FAILURE_POINTER (regend[this_reg]); \ + \ + DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ + DEBUG_PRINT2 (" match_null=%d", \ + REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ + DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ + DEBUG_PRINT2 (" matched_something=%d", \ + MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT2 (" ever_matched=%d", \ + EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT1 ("\n"); \ + PUSH_FAILURE_ELT (reg_info[this_reg].word); \ + } \ \ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\ - PUSH_FAILURE_ITEM (lowest_active_reg); \ + PUSH_FAILURE_INT (lowest_active_reg); \ \ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\ - PUSH_FAILURE_ITEM (highest_active_reg); \ + PUSH_FAILURE_INT (highest_active_reg); \ \ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ - PUSH_FAILURE_ITEM (pattern_place); \ + PUSH_FAILURE_POINTER (pattern_place); \ \ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ size2); \ DEBUG_PRINT1 ("'\n"); \ - PUSH_FAILURE_ITEM (string_place); \ + PUSH_FAILURE_POINTER (string_place); \ \ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ DEBUG_PUSH (failure_id); \ @@ -2458,7 +2478,7 @@ typedef struct LOW_REG, HIGH_REG -- the highest and lowest active registers. REGSTART, REGEND -- arrays of string positions. REG_INFO -- array of information about each subexpression. - + Also assumes the variables `fail_stack' and (if debugging), `bufp', `pend', `string1', `size1', `string2', and `size2'. */ @@ -2483,7 +2503,7 @@ typedef struct /* If the saved string location is NULL, it came from an \ on_failure_keep_string_jump opcode, and we want to throw away the \ saved NULL, thus retaining our current position in the string. */ \ - string_temp = POP_FAILURE_ITEM (); \ + string_temp = POP_FAILURE_POINTER (); \ if (string_temp != NULL) \ str = (const char *) string_temp; \ \ @@ -2491,29 +2511,40 @@ typedef struct DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ DEBUG_PRINT1 ("'\n"); \ \ - pat = (unsigned char *) POP_FAILURE_ITEM (); \ + pat = (unsigned char *) POP_FAILURE_POINTER (); \ DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ \ /* Restore register info. */ \ - high_reg = (unsigned) POP_FAILURE_ITEM (); \ + high_reg = (unsigned) POP_FAILURE_INT (); \ DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \ \ - low_reg = (unsigned) POP_FAILURE_ITEM (); \ + low_reg = (unsigned) POP_FAILURE_INT (); \ DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \ \ - for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ - { \ - DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ + if (1) \ + for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ + { \ + DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ \ - reg_info[this_reg].word = POP_FAILURE_ITEM (); \ - DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ + reg_info[this_reg].word = POP_FAILURE_ELT (); \ + DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ \ - regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \ - DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + regend[this_reg] = (const char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ \ - regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \ - DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + regstart[this_reg] = (const char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + } \ + else \ + { \ + for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \ + { \ + reg_info[this_reg].word.integer = 0; \ + regend[this_reg] = 0; \ + regstart[this_reg] = 0; \ + } \ + highest_active_reg = high_reg; \ } \ \ DEBUG_STATEMENT (nfailure_points_popped++); \ @@ -2526,7 +2557,7 @@ typedef struct The caller must supply the address of a (1 << BYTEWIDTH)-byte data area as BUFP->fastmap. - + We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in the pattern buffer. @@ -2543,11 +2574,11 @@ re_compile_fastmap (bufp) #endif /* We don't push any register information onto the failure stack. */ unsigned num_regs = 0; - + register char *fastmap = bufp->fastmap; unsigned char *pattern = bufp->buffer; unsigned long size = bufp->used; - const unsigned char *p = pattern; + unsigned char *p = pattern; register unsigned char *pend = pattern + size; /* Assume that each path through the pattern can be null until @@ -2560,12 +2591,12 @@ re_compile_fastmap (bufp) boolean succeed_n_p = false; assert (fastmap != NULL && p != NULL); - + INIT_FAIL_STACK (); bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ bufp->fastmap_accurate = 1; /* It will be when we're done. */ bufp->can_be_null = 0; - + while (p != pend || !FAIL_STACK_EMPTY ()) { if (p == pend) @@ -2575,7 +2606,7 @@ re_compile_fastmap (bufp) /* Reset for next path. */ path_can_be_null = true; - p = fail_stack.stack[--fail_stack.avail]; + p = fail_stack.stack[--fail_stack.avail].pointer; } /* We should never be about to go beyond the end of the pattern. */ @@ -2704,10 +2735,10 @@ re_compile_fastmap (bufp) case jump_past_alt: case dummy_failure_jump: EXTRACT_NUMBER_AND_INCR (j, p); - p += j; + p += j; if (j > 0) continue; - + /* Jump backward implies we just went through the body of a loop and matched nothing. Opcode jumped to should be `on_failure_jump' or `succeed_n'. Just treat it like an @@ -2719,11 +2750,11 @@ re_compile_fastmap (bufp) p++; EXTRACT_NUMBER_AND_INCR (j, p); - p += j; - + p += j; + /* If what's on the stack is where we are now, pop it. */ - if (!FAIL_STACK_EMPTY () - && fail_stack.stack[fail_stack.avail - 1] == p) + if (!FAIL_STACK_EMPTY () + && fail_stack.stack[fail_stack.avail - 1].pointer == p) fail_stack.avail--; continue; @@ -2760,7 +2791,7 @@ re_compile_fastmap (bufp) case succeed_n: /* Get to the number of times to succeed. */ - p += 2; + p += 2; /* Increment p past the n for when k != 0. */ EXTRACT_NUMBER_AND_INCR (k, p); @@ -2851,7 +2882,7 @@ re_search (bufp, string, size, startpos, range, regs) int size, startpos, range; struct re_registers *regs; { - return re_search_2 (bufp, NULL, 0, string, size, startpos, range, + return re_search_2 (bufp, NULL, 0, string, size, startpos, range, regs, size); } @@ -2859,17 +2890,17 @@ re_search (bufp, string, size, startpos, range, regs) /* Using the compiled pattern in BUFP->buffer, first tries to match the virtual concatenation of STRING1 and STRING2, starting first at index STARTPOS, then at STARTPOS + 1, and so on. - + STRING1 and STRING2 have length SIZE1 and SIZE2, respectively. - + RANGE is how far to scan while trying to match. RANGE = 0 means try only at STARTPOS; in general, the last start tried is STARTPOS + RANGE. - + In REGS, return the indices of the virtual concatenation of STRING1 and STRING2 that matched the entire BUFP->buffer and its contained subexpressions. - + Do not consider matching one past the index STOP in the virtual concatenation of STRING1 and STRING2. @@ -2896,7 +2927,7 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) /* Check for out-of-range STARTPOS. */ if (startpos < 0 || startpos > total_size) return -1; - + /* Fix up RANGE if it might eventually take us outside the virtual concatenation of STRING1 and STRING2. */ if (endpos < -1) @@ -2914,14 +2945,25 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) range = 1; } +#ifdef emacs + /* In a forward search for something that starts with \=. + don't keep searching past point. */ + if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == at_dot && range > 0) + { + range = PT - startpos; + if (range <= 0) + return -1; + } +#endif /* emacs */ + /* Update the fastmap now if not correct already. */ if (fastmap && !bufp->fastmap_accurate) if (re_compile_fastmap (bufp) == -2) return -2; - + /* Loop through the string, looking for a place to start matching. */ for (;;) - { + { /* If a fastmap is supplied, skip quickly over characters that cannot be the start of a match. If the pattern can match the null string, however, we don't need to skip characters; we want @@ -2938,7 +2980,7 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) lim = range - (size1 - startpos); d = (startpos >= size1 ? string2 - size1 : string1) + startpos; - + /* Written out as an if-else to avoid testing `translate' inside the loop. */ if (translate) @@ -2955,7 +2997,7 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) else /* Searching backwards. */ { register char c = (size1 == 0 || startpos >= size1 - ? string2[startpos - size1] + ? string2[startpos - size1] : string1[startpos]); if (!fastmap[(unsigned char) TRANSLATE (c)]) @@ -2972,21 +3014,21 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) startpos, regs, stop); if (val >= 0) return startpos; - + if (val == -2) return -2; advance: - if (!range) + if (!range) break; - else if (range > 0) + else if (range > 0) { - range--; + range--; startpos++; } else { - range++; + range++; startpos--; } } @@ -3001,16 +3043,16 @@ static boolean alt_match_null_string_p (), group_match_null_string_p (); /* Structure for per-register (a.k.a. per-group) information. - This must not be longer than one word, because we push this value - onto the failure stack. Other register information, such as the + Other register information, such as the starting and ending positions (which are addresses), and the list of inner groups (which is a bits list) are maintained in separate - variables. - + variables. + We are making a (strictly speaking) nonportable assumption here: that the compiler will pack our bit fields into something that fits into the type of `word', i.e., is something that fits into one item on the failure stack. */ + typedef union { fail_stack_elt_t word; @@ -3080,7 +3122,7 @@ typedef union /* Test if at very beginning or at very end of the virtual concatenation of `string1' and `string2'. If only one string, it's `string2'. */ #define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) -#define AT_STRINGS_END(d) ((d) == end2) +#define AT_STRINGS_END(d) ((d) == end2) /* Test if D points to a character which is word-constituent. We have @@ -3152,7 +3194,7 @@ re_match (bufp, string, size, pos, regs) the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 and SIZE2, respectively). We start matching at POS, and stop matching at STOP. - + If REGS is non-null and the `no_sub' field of BUFP is nonzero, we store offsets for the substring each group matched in REGS. See the documentation for exactly how many groups we fill. @@ -3183,7 +3225,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* Where we are in the data, and the end of the current string. */ const char *d, *dend; - + /* Where we are in the pattern, and the end of the pattern. */ unsigned char *p = bufp->buffer; register unsigned char *pend = p + bufp->used; @@ -3210,7 +3252,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) return, for use in backreferences. The number here includes an element for register zero. */ unsigned num_regs = bufp->re_nsub + 1; - + /* The currently active registers. */ unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG; unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG; @@ -3237,10 +3279,10 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) matched any of the pattern so far this time through the reg_num-th subexpression. These two fields get reset each time through any loop their register is in. */ - register_info_type *reg_info; + register_info_type *reg_info; /* The following record the register info as found in the above - variables when we find a match better than any we've seen before. + variables when we find a match better than any we've seen before. This happens as we backtrack through the failure points, which in turn happens only if we have not yet matched the entire string. */ unsigned best_regs_set = false; @@ -3262,13 +3304,13 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) #ifdef DEBUG /* Counts the total number of registers pushed. */ - unsigned num_regs_pushed = 0; + unsigned num_regs_pushed = 0; #endif DEBUG_PRINT1 ("\n\nEntering re_match_2.\n"); - + INIT_FAIL_STACK (); - + /* Do not bother to initialize all the register variables if there are no groups in the pattern, as it takes a fair amount of time. If there are groups, we include space for register 0 (the whole @@ -3286,8 +3328,8 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) reg_dummy = REGEX_TALLOC (num_regs, const char *); reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type); - if (!(regstart && regend && old_regstart && old_regend && reg_info - && best_regstart && best_regend && reg_dummy && reg_info_dummy)) + if (!(regstart && regend && old_regstart && old_regend && reg_info + && best_regstart && best_regend && reg_dummy && reg_info_dummy)) { FREE_VARIABLES (); return -2; @@ -3310,21 +3352,21 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) FREE_VARIABLES (); return -1; } - + /* Initialize subexpression text positions to -1 to mark ones that no start_memory/stop_memory has been seen for. Also initialize the register information struct. */ for (mcnt = 1; mcnt < num_regs; mcnt++) { - regstart[mcnt] = regend[mcnt] + regstart[mcnt] = regend[mcnt] = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; - + REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; IS_ACTIVE (reg_info[mcnt]) = 0; MATCHED_SOMETHING (reg_info[mcnt]) = 0; EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0; } - + /* We move `string1' into `string2' if the latter's empty -- but not if `string1' is null. */ if (size2 == 0 && string1 != NULL) @@ -3349,7 +3391,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) end_match_2 = string2 + stop - size1; } - /* `p' scans through the pattern as `d' scans through the data. + /* `p' scans through the pattern as `d' scans through the data. `dend' is the end of the input string that `d' points within. `d' is advanced into the following input string whenever necessary, but this happens before fetching; therefore, at the beginning of the @@ -3371,7 +3413,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) DEBUG_PRINT1 ("The string to match is: `"); DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2); DEBUG_PRINT1 ("'\n"); - + /* This loops over pattern commands. It exits by returning from the function if the match is complete, or it drops through if the match fails at this starting point in the input data. */ @@ -3382,13 +3424,13 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) if (p == pend) { /* End of pattern means we might have succeeded. */ DEBUG_PRINT1 ("end of pattern ... "); - + /* If we haven't matched the entire string, and we want the longest match, try backtracking. */ if (d != end_match_2) { DEBUG_PRINT1 ("backtracking.\n"); - + if (!FAIL_STACK_EMPTY ()) { /* More failure points to try. */ boolean same_str_p = (FIRST_STRING_P (match_end) @@ -3401,16 +3443,16 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) { best_regs_set = true; match_end = d; - + DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); - + for (mcnt = 1; mcnt < num_regs; mcnt++) { best_regstart[mcnt] = regstart[mcnt]; best_regend[mcnt] = regend[mcnt]; } } - goto fail; + goto fail; } /* If no failure points, don't restore garbage. */ @@ -3423,7 +3465,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) strings `x-' and `y-z-', if the two strings are not consecutive in memory. */ DEBUG_PRINT1 ("Restoring best registers.\n"); - + d = match_end; dend = ((d >= string1 && d <= end1) ? end_match_1 : end_match_2); @@ -3467,7 +3509,11 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) } } else - assert (bufp->regs_allocated == REGS_FIXED); + { + /* These braces fend off a "empty body in an else-statement" + warning under GCC when assert expands to nothing. */ + assert (bufp->regs_allocated == REGS_FIXED); + } /* Convert the pointer data in `regstart' and `regend' to indices. Register zero has to be set differently, @@ -3478,7 +3524,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1 : d - string2 + size1); } - + /* Go through the first `min (num_regs, regs->num_regs)' registers, since that is all we initialized. */ for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++) @@ -3491,7 +3537,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]); } } - + /* If the regs structure we return has more elements than were in the pattern, set the extra elements to -1. If we (re)allocated the registers, this is the case, @@ -3507,8 +3553,8 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) nfailure_points_pushed - nfailure_points_popped); DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); - mcnt = d - pos - (MATCHING_IN_FIRST_STRING - ? string1 + mcnt = d - pos - (MATCHING_IN_FIRST_STRING + ? string1 : string2 - size1); DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); @@ -3598,7 +3644,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) p += 1 + *p; if (!not) goto fail; - + SET_REGS_MATCHED (); d++; break; @@ -3615,9 +3661,9 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* Find out if this group can match the empty string. */ p1 = p; /* To send to group_match_null_string_p. */ - + if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) - REG_MATCH_NULL_STRING_P (reg_info[*p]) + REG_MATCH_NULL_STRING_P (reg_info[*p]) = group_match_null_string_p (&p1, pend, reg_info); /* Save the position in the string where we were the last time @@ -3628,7 +3674,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) ? REG_UNSET (regstart[*p]) ? d : regstart[*p] : regstart[*p]; - DEBUG_PRINT2 (" old_regstart: %d\n", + DEBUG_PRINT2 (" old_regstart: %d\n", POINTER_TO_OFFSET (old_regstart[*p])); regstart[*p] = d; @@ -3636,10 +3682,10 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) IS_ACTIVE (reg_info[*p]) = 1; MATCHED_SOMETHING (reg_info[*p]) = 0; - + /* This is the new highest active register. */ highest_active_reg = *p; - + /* If nothing was active before, this is the new lowest active register. */ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) @@ -3655,7 +3701,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) number, and the number of inner groups. */ case stop_memory: DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]); - + /* We need to save the string position the last time we were at this close-group operator in case the group is operated upon by a repetition operator, e.g., with `((a*)*(b*)*)*' @@ -3664,7 +3710,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) ? REG_UNSET (regend[*p]) ? d : regend[*p] : regend[*p]; - DEBUG_PRINT2 (" old_regend: %d\n", + DEBUG_PRINT2 (" old_regend: %d\n", POINTER_TO_OFFSET (old_regend[*p])); regend[*p] = d; @@ -3672,7 +3718,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* This register isn't active anymore. */ IS_ACTIVE (reg_info[*p]) = 0; - + /* If this was the only register active, nothing is active anymore. */ if (lowest_active_reg == highest_active_reg) @@ -3688,7 +3734,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) unsigned char r = *p - 1; while (r > 0 && !IS_ACTIVE (reg_info[r])) r--; - + /* If we end up at register zero, that means that we saved the registers as the result of an `on_failure_jump', not a `start_memory', and we jumped to past the innermost @@ -3704,7 +3750,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) else highest_active_reg = r; } - + /* If just failed to match something this time around with a group that's operated on by a repetition operator, try to force exit from the ``loop'', and restore the register @@ -3715,7 +3761,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) && (p + 2) < pend) { boolean is_a_jump_n = false; - + p1 = p + 2; mcnt = 0; switch ((re_opcode_t) *p1++) @@ -3730,12 +3776,12 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) if (is_a_jump_n) p1 += 2; break; - + default: /* do nothing */ ; } p1 += mcnt; - + /* If the next operation is a jump backwards in the pattern to an on_failure_jump right before the start_memory corresponding to this stop_memory, exit from the loop @@ -3749,17 +3795,17 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) failed match, e.g., with `(a*)*b' against `ab' for regstart[1], and, e.g., with `((a*)*(b*)*)*' against `aba' for regend[3]. - + Also restore the registers for inner groups for, e.g., `((a*)(b*))*' against `aba' (register 3 would otherwise get trashed). */ - + if (EVER_MATCHED_SOMETHING (reg_info[*p])) { - unsigned r; - + unsigned r; + EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; - + /* Restore this and inner groups' (if any) registers. */ for (r = *p; r < *p + *(p + 1); r++) { @@ -3768,7 +3814,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* xx why this test? */ if ((int) old_regend[r] >= (int) regstart[r]) regend[r] = old_regend[r]; - } + } } p1++; EXTRACT_NUMBER_AND_INCR (mcnt, p1); @@ -3777,7 +3823,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) goto fail; } } - + /* Move past the register number and the inner group count. */ p += 2; break; @@ -3794,16 +3840,16 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* Can't back reference a group which we've never matched. */ if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) goto fail; - + /* Where in input to try to start matching. */ d2 = regstart[regno]; - + /* Where to stop matching; if both the place to start and the place to stop matching are in the same string, then set to the place to stop, otherwise, for now have to use the end of the first string. */ - dend2 = ((FIRST_STRING_P (regstart[regno]) + dend2 = ((FIRST_STRING_P (regstart[regno]) == FIRST_STRING_P (regend[regno])) ? regend[regno] : end_match_1); for (;;) @@ -3827,16 +3873,16 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* How many characters left in this segment to match. */ mcnt = dend - d; - + /* Want how many consecutive characters we can match in one shot, so, if necessary, adjust the count. */ if (mcnt > dend2 - d2) mcnt = dend2 - d2; - + /* Compare that many; failure if mismatch, else move past them. */ - if (translate - ? bcmp_translate (d, d2, mcnt, translate) + if (translate + ? bcmp_translate (d, d2, mcnt, translate) : bcmp (d, d2, mcnt)) goto fail; d += mcnt, d2 += mcnt; @@ -3850,7 +3896,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) `newline_anchor' is set, after newlines. */ case begline: DEBUG_PRINT1 ("EXECUTING begline.\n"); - + if (AT_STRINGS_BEG (d)) { if (!bufp->not_bol) break; @@ -3871,7 +3917,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) { if (!bufp->not_eol) break; } - + /* We have to ``prefetch'' the next character. */ else if ((d == end1 ? *string2 : *d) == '\n' && bufp->newline_anchor) @@ -3905,7 +3951,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) then the . fails against the \n. But the next thing we want to do is match the \n against the \n; if we restored the string value, we would be back at the foo. - + Because this is used only in specific cases, we don't need to check all the things that `on_failure_jump' does, to make sure the right things get saved on the stack. Hence we don't @@ -3915,7 +3961,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) case; that seems worse than this. */ case on_failure_keep_string_jump: DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); - + EXTRACT_NUMBER_AND_INCR (mcnt, p); DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); @@ -3924,7 +3970,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* Uses of on_failure_jump: - + Each alternative starts with an on_failure_jump that points to the beginning of the next alternative. Each alternative except the last ends with a jump that in effect jumps past @@ -3990,7 +4036,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) would have to backtrack because of (as in, e.g., `a*a') then we can change to pop_failure_jump, because we'll never have to backtrack. - + This is not true in the case of alternatives: in `(a|ab)*' we do need to backtrack to the `ab' alternative (e.g., if the string was `ab'). But instead of trying to @@ -4030,12 +4076,12 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", c, p1[5]); } - + else if ((re_opcode_t) p1[3] == charset || (re_opcode_t) p1[3] == charset_not) { int not = (re_opcode_t) p1[3] == charset_not; - + if (c < (unsigned char) (p1[4] * BYTEWIDTH) && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; @@ -4084,7 +4130,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) } /* Note fall through. */ - + /* Unconditionally jump (without popping any failure points). */ case jump: unconditional_jump: @@ -4094,7 +4140,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) DEBUG_PRINT2 ("(to 0x%x).\n", p); break; - + /* We need this opcode so we can detect where alternatives end in `group_match_null_string_p' et al. */ case jump_past_alt: @@ -4129,7 +4175,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* Have to succeed matching what follows at least n times. After that, handle like `on_failure_jump'. */ - case succeed_n: + case succeed_n: EXTRACT_NUMBER (mcnt, p + 2); DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); @@ -4150,8 +4196,8 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) goto on_failure; } break; - - case jump_n: + + case jump_n: EXTRACT_NUMBER (mcnt, p + 2); DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); @@ -4160,13 +4206,13 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) { mcnt--; STORE_NUMBER (p + 2, mcnt); - goto unconditional_jump; + goto unconditional_jump; } /* If don't have to jump any more, skip over the rest of command. */ - else - p += 4; + else + p += 4; break; - + case set_number_at: { DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); @@ -4205,31 +4251,23 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) goto fail; #ifdef emacs -#ifdef emacs19 case before_dot: DEBUG_PRINT1 ("EXECUTING before_dot.\n"); if (PTR_CHAR_POS ((unsigned char *) d) >= point) goto fail; break; - + case at_dot: DEBUG_PRINT1 ("EXECUTING at_dot.\n"); if (PTR_CHAR_POS ((unsigned char *) d) != point) goto fail; break; - + case after_dot: DEBUG_PRINT1 ("EXECUTING after_dot.\n"); if (PTR_CHAR_POS ((unsigned char *) d) <= point) goto fail; break; -#else /* not emacs19 */ - case at_dot: - DEBUG_PRINT1 ("EXECUTING at_dot.\n"); - if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point) - goto fail; - break; -#endif /* not emacs19 */ case syntaxspec: DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); @@ -4243,6 +4281,10 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) PREFETCH (); if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail; + /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ + d++; + if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt) + goto fail; SET_REGS_MATCHED (); break; @@ -4256,8 +4298,10 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) mcnt = (int) Sword; matchnotsyntax: PREFETCH (); - if (SYNTAX (*d++) == (enum syntaxcode) mcnt) - goto fail; + /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ + d++; + if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt) + goto fail; SET_REGS_MATCHED (); break; @@ -4270,7 +4314,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) SET_REGS_MATCHED (); d++; break; - + case notwordchar: DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n"); PREFETCH (); @@ -4280,7 +4324,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) d++; break; #endif /* not emacs */ - + default: abort (); } @@ -4305,7 +4349,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) if (p < pend) { boolean is_a_jump_n = false; - + /* If failed to a backwards jump that's part of a repetition loop, need to pop this failure point and use the next one. */ switch ((re_opcode_t) *p) @@ -4317,7 +4361,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) case jump: p1 = p + 1; EXTRACT_NUMBER_AND_INCR (mcnt, p1); - p1 += mcnt; + p1 += mcnt; if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n) || (!is_a_jump_n @@ -4348,10 +4392,10 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* We are passed P pointing to a register number after a start_memory. - + Return true if the pattern up to the corresponding stop_memory can match the empty string, and false otherwise. - + If we find the matching stop_memory, sets P to point to one past its number. Otherwise, sets P to an undefined byte less than or equal to END. @@ -4365,20 +4409,20 @@ group_match_null_string_p (p, end, reg_info) int mcnt; /* Point to after the args to the start_memory. */ unsigned char *p1 = *p + 2; - + while (p1 < end) { /* Skip over opcodes that can match nothing, and return true or false, as appropriate, when we get to one that can't, or to the matching stop_memory. */ - + switch ((re_opcode_t) *p1) { /* Could be either a loop or a series of alternatives. */ case on_failure_jump: p1++; EXTRACT_NUMBER_AND_INCR (mcnt, p1); - + /* If the next operation is not a jump backwards in the pattern. */ @@ -4392,7 +4436,7 @@ group_match_null_string_p (p, end, reg_info) /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3 - /exactn/1/c + /exactn/1/c So, we have to first go through the first (n-1) alternatives and then deal with the last one separately. */ @@ -4408,19 +4452,19 @@ group_match_null_string_p (p, end, reg_info) is, including the ending `jump_past_alt' and its number. */ - if (!alt_match_null_string_p (p1, p1 + mcnt - 3, + if (!alt_match_null_string_p (p1, p1 + mcnt - 3, reg_info)) return false; /* Move to right after this alternative, including the jump_past_alt. */ - p1 += mcnt; + p1 += mcnt; /* Break if it's the beginning of an n-th alternative that doesn't begin with an on_failure_jump. */ if ((re_opcode_t) *p1 != on_failure_jump) break; - + /* Still have to check that it's not an n-th alternative that starts with an on_failure_jump. */ p1++; @@ -4445,14 +4489,14 @@ group_match_null_string_p (p, end, reg_info) } /* if mcnt > 0 */ break; - + case stop_memory: assert (p1[1] == **p); *p = p1 + 2; return true; - - default: + + default: if (!common_op_match_null_string_p (&p1, end, reg_info)) return false; } @@ -4465,7 +4509,7 @@ group_match_null_string_p (p, end, reg_info) /* Similar to group_match_null_string_p, but doesn't deal with alternatives: It expects P to be the first byte of a single alternative and END one byte past the last. The alternative can contain groups. */ - + static boolean alt_match_null_string_p (p, end, reg_info) unsigned char *p, *end; @@ -4473,12 +4517,12 @@ alt_match_null_string_p (p, end, reg_info) { int mcnt; unsigned char *p1 = p; - + while (p1 < end) { - /* Skip over opcodes that can match nothing, and break when we get + /* Skip over opcodes that can match nothing, and break when we get to one that can't. */ - + switch ((re_opcode_t) *p1) { /* It's a loop. */ @@ -4487,8 +4531,8 @@ alt_match_null_string_p (p, end, reg_info) EXTRACT_NUMBER_AND_INCR (mcnt, p1); p1 += mcnt; break; - - default: + + default: if (!common_op_match_null_string_p (&p1, end, reg_info)) return false; } @@ -4499,8 +4543,8 @@ alt_match_null_string_p (p, end, reg_info) /* Deals with the ops common to group_match_null_string_p and - alt_match_null_string_p. - + alt_match_null_string_p. + Sets P to one after the op and its arguments, if any. */ static boolean @@ -4535,7 +4579,7 @@ common_op_match_null_string_p (p, end, reg_info) reg_no = *p1; assert (reg_no > 0 && reg_no <= MAX_REGNUM); ret = group_match_null_string_p (&p1, end, reg_info); - + /* Have to set this here in case we're checking a group which contains a group and a back reference to it. */ @@ -4545,7 +4589,7 @@ common_op_match_null_string_p (p, end, reg_info) if (!ret) return false; break; - + /* If this is an optimized succeed_n for zero times, make the jump. */ case jump: EXTRACT_NUMBER_AND_INCR (mcnt, p1); @@ -4557,7 +4601,7 @@ common_op_match_null_string_p (p, end, reg_info) case succeed_n: /* Get to the number of times to succeed. */ - p1 += 2; + p1 += 2; EXTRACT_NUMBER_AND_INCR (mcnt, p1); if (mcnt == 0) @@ -4570,7 +4614,7 @@ common_op_match_null_string_p (p, end, reg_info) return false; break; - case duplicate: + case duplicate: if (!REG_MATCH_NULL_STRING_P (reg_info[*p1])) return false; break; @@ -4590,7 +4634,7 @@ common_op_match_null_string_p (p, end, reg_info) /* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN bytes; nonzero otherwise. */ - + static int bcmp_translate (s1, s2, len, translate) unsigned char *s1, *s2; @@ -4611,10 +4655,10 @@ bcmp_translate (s1, s2, len, translate) /* re_compile_pattern is the GNU regular expression compiler: it compiles PATTERN (of length SIZE) and puts the result in BUFP. Returns 0 if the pattern was valid, otherwise an error string. - + Assumes the `allocated' (and perhaps `buffer') and `translate' fields are set in BUFP on entry. - + We call regex_compile to do the actual compilation. */ const char * @@ -4624,19 +4668,19 @@ re_compile_pattern (pattern, length, bufp) struct re_pattern_buffer *bufp; { reg_errcode_t ret; - + /* GNU code is written to assume at least RE_NREGS registers will be set (and at least one extra will be -1). */ bufp->regs_allocated = REGS_UNALLOCATED; - + /* And GNU code determines whether or not to get register information by passing null for the REGS argument to re_match, etc., not by setting no_sub. */ bufp->no_sub = 0; - + /* Match anchors at newline. */ bufp->newline_anchor = 1; - + ret = regex_compile (pattern, length, re_syntax_options, bufp); return re_error_msg[(int) ret]; @@ -4655,7 +4699,7 @@ re_comp (s) const char *s; { reg_errcode_t ret; - + if (!s) { if (!re_comp_buf.buffer) @@ -4739,7 +4783,7 @@ re_exec (s) int regcomp (preg, pattern, cflags) regex_t *preg; - const char *pattern; + const char *pattern; int cflags; { reg_errcode_t ret; @@ -4750,17 +4794,17 @@ regcomp (preg, pattern, cflags) /* regex_compile will allocate the space for the compiled pattern. */ preg->buffer = 0; preg->allocated = 0; - + /* Don't bother to use a fastmap when searching. This simplifies the REG_NEWLINE case: if we used a fastmap, we'd have to put all the characters after newlines into the fastmap. This way, we just try every character. */ preg->fastmap = 0; - + if (cflags & REG_ICASE) { unsigned i; - + preg->translate = (char *) malloc (CHAR_SET_SIZE); if (preg->translate == NULL) return (int) REG_ESPACE; @@ -4785,38 +4829,38 @@ regcomp (preg, pattern, cflags) preg->no_sub = !!(cflags & REG_NOSUB); - /* POSIX says a null character in the pattern terminates it, so we + /* POSIX says a null character in the pattern terminates it, so we can use strlen here in compiling the pattern. */ ret = regex_compile (pattern, strlen (pattern), syntax, preg); - + /* POSIX doesn't distinguish between an unmatched open-group and an unmatched close-group: both are REG_EPAREN. */ if (ret == REG_ERPAREN) ret = REG_EPAREN; - + return (int) ret; } /* regexec searches for a given pattern, specified by PREG, in the string STRING. - + If NMATCH is zero or REG_NOSUB was set in the cflags argument to `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at least NMATCH elements, and we set them to the offsets of the corresponding matched substrings. - + EFLAGS specifies `execution flags' which affect matching: if REG_NOTBOL is set, then ^ does not match at the beginning of the string; if REG_NOTEOL is set, then $ does not match at the end. - + We return 0 if we find a match and REG_NOMATCH if not. */ int regexec (preg, string, nmatch, pmatch, eflags) const regex_t *preg; - const char *string; - size_t nmatch; - regmatch_t pmatch[]; + const char *string; + size_t nmatch; + regmatch_t pmatch[]; int eflags; { int ret; @@ -4826,15 +4870,15 @@ regexec (preg, string, nmatch, pmatch, eflags) boolean want_reg_info = !preg->no_sub && nmatch > 0; private_preg = *preg; - + private_preg.not_bol = !!(eflags & REG_NOTBOL); private_preg.not_eol = !!(eflags & REG_NOTEOL); - + /* The user has told us exactly how many registers to return information about, via `nmatch'. We have to pass that on to the matching routines. */ private_preg.regs_allocated = REGS_FIXED; - + if (want_reg_info) { regs.num_regs = nmatch; @@ -4848,7 +4892,7 @@ regexec (preg, string, nmatch, pmatch, eflags) ret = re_search (&private_preg, string, len, /* start: */ 0, /* range: */ len, want_reg_info ? ®s : (struct re_registers *) 0); - + /* Copy the register information to the POSIX structure. */ if (want_reg_info) { @@ -4888,7 +4932,7 @@ regerror (errcode, preg, errbuf, errbuf_size) if (errcode < 0 || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0]))) - /* Only error codes returned by the rest of the code should be passed + /* Only error codes returned by the rest of the code should be passed to this routine. If we are given anything else, or if other regex code generates an invalid error code, then the program has a bug. Dump core so we can fix it. */ @@ -4902,7 +4946,7 @@ regerror (errcode, preg, errbuf, errbuf_size) msg = "Success"; msg_size = strlen (msg) + 1; /* Includes the null. */ - + if (errbuf_size != 0) { if (msg_size > errbuf_size) @@ -4927,7 +4971,7 @@ regfree (preg) if (preg->buffer != NULL) free (preg->buffer); preg->buffer = NULL; - + preg->allocated = 0; preg->used = 0; @@ -4942,11 +4986,3 @@ regfree (preg) } #endif /* not emacs */ - -/* -Local variables: -make-backup-files: t -version-control: t -trim-versions-without-asking: nil -End: -*/ diff --git a/contrib/cvs/lib/regex.h b/contrib/cvs/lib/regex.h index 408dd21..6ab52c3 100644 --- a/contrib/cvs/lib/regex.h +++ b/contrib/cvs/lib/regex.h @@ -11,11 +11,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifndef __REGEXP_LIBRARY_H__ #define __REGEXP_LIBRARY_H__ @@ -465,9 +461,11 @@ extern void re_set_registers _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, unsigned num_regs, regoff_t *starts, regoff_t *ends)); -/* 4.2 bsd compatibility. */ -extern char *re_comp _RE_ARGS ((const char *)); -extern int re_exec _RE_ARGS ((const char *)); +/* 4.2 bsd compatibility. System headers may declare the argument as + either "char *" (e.g. Cray unistd.h) or "const char *" (e.g. linux + regex.h), so don't prototype them here. */ +extern char *re_comp (); +extern int re_exec (); /* POSIX compatibility. */ extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags)); diff --git a/contrib/cvs/lib/rename.c b/contrib/cvs/lib/rename.c index ce2805b..ae3d36e 100644 --- a/contrib/cvs/lib/rename.c +++ b/contrib/cvs/lib/rename.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/cvs/lib/sighandle.c b/contrib/cvs/lib/sighandle.c index ace7db3..b229ea6 100644 --- a/contrib/cvs/lib/sighandle.c +++ b/contrib/cvs/lib/sighandle.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ /* Written by Paul Sander, HaL Computer Systems, Inc. <paul@hal.com> Brian Berliner <berliner@Sun.COM> added POSIX support */ @@ -30,8 +26,6 @@ * must not themselves make calls to the signal handling * facilities. * - * $CVSid: @(#)sighandle.c 1.13 94/10/07 $ - * *************************************************************************/ #ifdef HAVE_CONFIG_H diff --git a/contrib/cvs/lib/strdup.c b/contrib/cvs/lib/strdup.c index 46fc8a0..c81969d 100644 --- a/contrib/cvs/lib/strdup.c +++ b/contrib/cvs/lib/strdup.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/cvs/lib/strerror.c b/contrib/cvs/lib/strerror.c index b0bec13..9e799b6 100644 --- a/contrib/cvs/lib/strerror.c +++ b/contrib/cvs/lib/strerror.c @@ -11,12 +11,7 @@ version 2 of the License, or (at your option) any later version. Libiberty is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with libiberty; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 675 Mass Ave, -Cambridge, MA 02139, USA. */ +Library General Public License for more details. */ #include "config.h" diff --git a/contrib/cvs/lib/stripslash.c b/contrib/cvs/lib/stripslash.c index 265950e..ece8ec8 100644 --- a/contrib/cvs/lib/stripslash.c +++ b/contrib/cvs/lib/stripslash.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/cvs/lib/system.h b/contrib/cvs/lib/system.h index 363124d..05025fe 100644 --- a/contrib/cvs/lib/system.h +++ b/contrib/cvs/lib/system.h @@ -9,13 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* $CVSid: @(#)system.h 1.18 94/09/25 $ */ + GNU General Public License for more details. */ #include <sys/types.h> #include <sys/stat.h> @@ -270,18 +264,20 @@ extern long timezone; check for struct utimbuf, but for now I'm checking NeXT here (so I don't have to debug the configure check across all the machines). */ #if defined (HAVE_UTIME_H) && !defined (NeXT) -#include <utime.h> -#elif defined (HAVE_SYS_UTIME_H) -# include <sys/utime.h> +# include <utime.h> #else -#ifndef ALTOS +# if defined (HAVE_SYS_UTIME_H) +# include <sys/utime.h> +# else +# ifndef ALTOS struct utimbuf { long actime; long modtime; }; -#endif +# endif int utime (); +# endif #endif #if STDC_HEADERS || HAVE_STRING_H @@ -295,7 +291,9 @@ int utime (); /* memory.h and strings.h conflict on some systems. */ #endif /* not STDC_HEADERS and not HAVE_STRING_H */ +#ifndef ERRNO_H_MISSING #include <errno.h> +#endif /* Not all systems set the same error code on a non-existent-file error. This tries to ask the question somewhat portably. @@ -422,14 +420,55 @@ char *getwd (); #define S_IWOTH 0000002 /* write permission, other */ #endif -/* Under MS-DOS and its derivatives (like Windows NT), mkdir takes only one - argument; permission is handled very differently on those systems than in - in Unix. So we leave such systems a hook on which they can hang their - own definitions. */ +/* Under non-UNIX operating systems (MS-DOS, WinNT, MacOS), many filesystem + calls take only one argument; permission is handled very differently on + those systems than in Unix. So we leave such systems a hook on which they + can hang their own definitions. */ + +#ifndef CVS_ACCESS +#define CVS_ACCESS access +#endif + +#ifndef CVS_CHDIR +#define CVS_CHDIR chdir +#endif + +#ifndef CVS_CREAT +#define CVS_CREAT creat +#endif + +#ifndef CVS_FOPEN +#define CVS_FOPEN fopen +#endif + #ifndef CVS_MKDIR #define CVS_MKDIR mkdir #endif +#ifndef CVS_OPEN +#define CVS_OPEN open +#endif + +#ifndef CVS_OPENDIR +#define CVS_OPENDIR opendir +#endif + +#ifndef CVS_RENAME +#define CVS_RENAME rename +#endif + +#ifndef CVS_RMDIR +#define CVS_RMDIR rmdir +#endif + +#ifndef CVS_STAT +#define CVS_STAT stat +#endif + +#ifndef CVS_UNLINK +#define CVS_UNLINK unlink +#endif + /* Some file systems are case-insensitive. If FOLD_FN_CHAR is #defined, it maps the character C onto its "canonical" form. In a case-insensitive system, it would map all alphanumeric characters diff --git a/contrib/cvs/lib/vasprintf.c b/contrib/cvs/lib/vasprintf.c index 45253b1..3dcfaaa 100644 --- a/contrib/cvs/lib/vasprintf.c +++ b/contrib/cvs/lib/vasprintf.c @@ -11,12 +11,7 @@ version 2 of the License, or (at your option) any later version. Libiberty is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with libiberty; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 675 Mass Ave, -Cambridge, MA 02139, USA. */ +Library General Public License for more details. */ #include <stdio.h> #include <string.h> @@ -31,14 +26,17 @@ Cambridge, MA 02139, USA. */ #include <varargs.h> #endif -extern int abs (); - #ifdef TEST int global_total_width; #endif -unsigned long strtoul (); -char *malloc (); +#ifdef STDC_HEADERS +#include <stdlib.h> +#else +extern int abs (); +extern unsigned long strtoul (); +extern char *malloc (); +#endif static int int_vasprintf (result, format, args) @@ -66,7 +64,7 @@ int_vasprintf (result, format, args) total_width += abs (va_arg (ap, int)); } else - total_width += strtoul (p, &p, 10); + total_width += strtoul (p, (char **)&p, 10); if (*p == '.') { ++p; @@ -76,7 +74,7 @@ int_vasprintf (result, format, args) total_width += abs (va_arg (ap, int)); } else - total_width += strtoul (p, &p, 10); + total_width += strtoul (p, (char **)&p, 10); } while (strchr ("hlL", *p)) ++p; @@ -126,7 +124,13 @@ vasprintf (result, format, args) const char *format; va_list args; { +#ifdef VA_LIST_IS_ARRAY + /* If va_list is an array, we do not need the additional indirection */ + return int_vasprintf (result, format, args); +#else + /* va_list is some sort of pointer */ return int_vasprintf (result, format, &args); +#endif } #ifdef TEST diff --git a/contrib/cvs/lib/wait.h b/contrib/cvs/lib/wait.h index db60434..60ed47a 100644 --- a/contrib/cvs/lib/wait.h +++ b/contrib/cvs/lib/wait.h @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifdef HAVE_SYS_WAIT_H #include <sys/types.h> /* For pid_t. */ @@ -21,12 +17,23 @@ #include <sys/resource.h> /* for rusage */ #endif #include <sys/wait.h> -#else +#endif +#ifndef WIFSTOPPED #define WIFSTOPPED(w) (((w) & 0xff) == 0x7f) +#endif +#ifndef WIFSIGNALED #define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0) +#endif +#ifndef WIFEXITED #define WIFEXITED(w) (((w) & 0xff) == 0) +#endif +#ifndef WSTOPSIG #define WSTOPSIG(w) (((w) >> 8) & 0xff) +#endif +#ifndef WTERMSIG #define WTERMSIG(w) ((w) & 0x7f) +#endif +#ifndef WEXITSTATUS #define WEXITSTATUS(w) (((w) >> 8) & 0xff) #endif diff --git a/contrib/cvs/lib/waitpid.c b/contrib/cvs/lib/waitpid.c index e8ddeb8..02d6acb 100644 --- a/contrib/cvs/lib/waitpid.c +++ b/contrib/cvs/lib/waitpid.c @@ -63,7 +63,11 @@ pid_t waitpid (pid, status, options) while (1) { +#ifdef HAVE_WAIT3 pid_t p = wait3 (status, options, (struct rusage *) 0); +#else + pid_t p = wait (status); +#endif if (p == 0 || p == -1 || p == pid) return p; diff --git a/contrib/cvs/lib/xgetwd.c b/contrib/cvs/lib/xgetwd.c index 8fe4ec1..e2a3baa 100644 --- a/contrib/cvs/lib/xgetwd.c +++ b/contrib/cvs/lib/xgetwd.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ /* Derived from xgetcwd.c in e.g. the GNU sh-utils. */ diff --git a/contrib/cvs/lib/yesno.c b/contrib/cvs/lib/yesno.c index 86b0798..1e87938 100644 --- a/contrib/cvs/lib/yesno.c +++ b/contrib/cvs/lib/yesno.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/contrib/cvs/man/ChangeLog b/contrib/cvs/man/ChangeLog index 69017c9..6023631 100644 --- a/contrib/cvs/man/ChangeLog +++ b/contrib/cvs/man/ChangeLog @@ -1,3 +1,51 @@ +Wed Feb 12 14:20:43 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (install): Depend on installdirs. + +Thu Jan 2 13:30:56 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in, cvsbug.8: Remove "675" paragraph; + see ../ChangeLog for rationale. + +Sat Nov 30 14:49:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.1: Add note at beginning about how cvs.texinfo is more + complete than this document. Without some such indication, users + have no way of knowing which document to consult. + +Tue Oct 1 14:01:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.1: Revert all changes by Greg Woods since CVS 1.8.86. They + are for new features which are not appropriate at this stage of + the release process. + +Mon Sep 30 18:21:05 1996 Greg A. Woods <woods@most.weird.com> + + * cvs.1: document -D, -g, DIFFBIN, and GREPBIN. + +Mon Sep 16 23:22:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvsbug.8: Remove references to cvsbug.el (or other aspects of + emacs interface) and cvsbug.info (or other aspects of texinfo + documentation). Neither one has ever existed. + +Wed Jul 24 19:01:35 1996 Ian Lance Taylor <ian@cygnus.com> + + * cvs.1: Document -x. + +Fri Sep 13 11:01:59 1996 Greg A. Woods <woods@clapton.seachange.com> + + * cvs.1: add description of -k and -K for rdiff. Mention that + import allows '-b 1' to import to the trunk. + +Tue May 14 10:23:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.1: Add cvs.texinfo to the SEE ALSO section. Replace an out + of date list of default ignore patterns with a reference to + cvs.texinfo (if someone wants to undo this change that is OK with + me but I did it this way because it isn't clear people would + actually keep cvs.1 up to date). + Wed Mar 13 17:06:39 1996 Jim Kingdon <kingdon@harvey.cyclic.com> * cvsinit.8: Removed. diff --git a/contrib/cvs/man/Makefile.in b/contrib/cvs/man/Makefile.in index 894e4d78..ffc3613 100644 --- a/contrib/cvs/man/Makefile.in +++ b/contrib/cvs/man/Makefile.in @@ -12,10 +12,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - SHELL = /bin/sh srcdir = @srcdir@ @@ -39,10 +35,9 @@ man8dir = $(mandir)/man8 all: .PHONY: all -# This used to depend on installdirs, but someone took it out (I think -# maybe it is/was some kind of Cygnus convention to not depend on installdirs; -# I'm not sure). I don't know what the pro(s) and con(s) are. -install: all +# This needs to depend on installdirs so that (for example) the +# install will not fail if $(prefix)/man/man5 does not yet exist. +install: all installdirs for f in $(MAN1FILES); do \ $(INSTALL_DATA) $(srcdir)/$$f $(man1dir)/$$f; \ done diff --git a/contrib/cvs/man/cvsbug.8 b/contrib/cvs/man/cvsbug.8 index 496ef14..63574d4 100644 --- a/contrib/cvs/man/cvsbug.8 +++ b/contrib/cvs/man/cvsbug.8 @@ -16,10 +16,6 @@ .\" 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 Library General Public -.\" License along with this program; if not, write to the Free -.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA -.\" .\" --------------------------------------------------------------------------- .nh .TH CVSBUG 1 xVERSIONx "February 1993" @@ -211,33 +207,11 @@ code. /tmp/pf$$ copy of empty PR form, for testing purposes .br /tmp/pbad$$ file for rejected PRs -.SH EMACS USER INTERFACE -An Emacs user interface for -.B cvsbug -with completion of field values is part of the -.B cvsbug -distribution (invoked with -.BR "M-x cvsbug" ). -See the file -.B cvsbug.info -or the ASCII file -.B INSTALL -in the top level directory of the distribution for configuration and -installation information. The Emacs LISP template file is -.B cvsbug-el.in -and is installed as -.BR cvsbug.el . .SH INSTALLATION AND CONFIGURATION See -.B cvsbug.info -or .B INSTALL for installation instructions. .SH SEE ALSO -.I Reporting Problems Using cvsbug -(also installed as the GNU Info file -.BR cvsbug.info ). -.LP .BR gnats (l), .BR query-pr (1), .BR edit-pr (1), diff --git a/contrib/cvs/src/ChangeLog b/contrib/cvs/src/ChangeLog index 99161b0..e1d4dc0 100644 --- a/contrib/cvs/src/ChangeLog +++ b/contrib/cvs/src/ChangeLog @@ -1,1373 +1,1217 @@ -Sun May 5 21:39:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Thu May 15 11:50:15 1997 Norbert Kiesel <nk@cosa.de> - * vers_ts.c (Version_TS): If sdtp is NULL, go ahead and check - RCS_getexpand for options. Fixes binaries and non-unix clients. - * sanity.sh: Fix binfiles-5.5 to test for the correct behavior - rather than the buggy behavior which existed when the binfiles-5.5 - test was written. - (binfiles-14c,binfiles-14f): Likewise. + * diff.c (diff_fileproc): cosmetic change (whitespace added). -Sun May 5 17:38:21 1996 Benjamin J. Lee <benjamin@cyclic.com> + * checkout.c (checkout): move local variable definition into the + block where the variable is used. - Integrated changes submitted by Ian Taylor <ian@cygnus.com> + * client.c (update_entries): initialize some local variables to shut up + gcc -O -Wall. - * update.c (update_dirent_proc): cvs co -p doesn't print - anything when run from an empty directory. + * buffer.c (buf_read_line): initialize a local variable to shut up + gcc -O -Wall. + - * import.c (import_descend_dir): Check for a file in the - repository which will be checked out to the same name as the - directory. +Wed May 14 16:29:50 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Thu May 2 13:34:37 1996 Benjamin J. Lee <benjamin@cyclic.com> + * admin.c (admin): When sending options to server, don't try to + send av[ac]. It may contain one of the names that we'll send in + send_file_names (which caused tests like keyword-6 to work, + sort of accidentally), or it may contain NULL (which would tend to + cause a coredump). + * sanity.sh (basicb): New test basicb-21 tests for above fix. - * Version 1.7.88 +Mon May 12 16:22:00 1997 Larry Jones <larry.jones@sdrc.com> -Thu May 2 01:40:55 1996 Benjamin J. Lee <benjamin@cyclic.com> + * add.c (add): Free message and repository in client code. + * checkout.c (checkout): Don't free repository unless allocated. + * client.c (start_rsh_server): Free command. - * server.c (HAVE_INITGROUPS): Use initgroups() only if - located by configure, in the event a system has crypt(), but - no initgroups() +Sun May 11 11:43:54 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed May 1 18:05:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * client.c: Remove all references to USE_DIRECT_TCP; see + ../ChangeLog for rationale. - * sanity.sh (basica): When testing rejection of reserved tag name, - use BASE instead of RESERVED. +Fri May 9 22:19:36 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed May 1 15:15:11 1996 Tom Jarmolowski <tjj@booklink.com> + * main.c (main): Add comment explaining why we call exit. Pass 0 + not EXIT_SUCCESS, because lib/system.h has portability cruft for + EXIT_FAILURE but not EXIT_SUCCESS. - * rcs.c (linevector_delete): Only copy up to vec->nlines - nlines, - not to vec->nlines. +Fri May 9 17:25:00 1997 Larry Jones <larry.jones@sdrc.com> -Wed May 1 15:43:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + Fix miscellaneous memory allocation problems: + * add.c (add): Free repository. + * client.c (notified_a_file): Free getline buffer. + * edit.c (notify_check): Free getline buffer. + * hash.c (dellist): Free header node when not caching. + * login.c (login): Don't continually free & allocate getline + buffer, use xstrdup instead of xmalloc/strcpy, free getline + buffer before returning. + * main.c (main): Call exit instead of returning so tools like + Purify won't consider permanently allocated memory as leaks. + * mkmodules.c (mkmodules): Free getline buffer. + * modules.c (cat_module): Call close_module. + * rcs.c (rcsvers_delproc): Free state. + * recurse.c (start_recursion): Free files_by_dir. + (unroll_files_proc): NULL out p->data after using it to set + filelist to avoid multiple frees. + * server.c (check_command_legal_p): Don't continually free & + allocate getline buffer, free getline buffer before returning. + (check_repository_password): Ditto, use xstrdup instead of + xmalloc/strcpy. + * wrapper.c (wrap_add_file): Free getline buffer. - * rcscmds.c (RCS_settag): Instead of reserving all tag names - containing only uppercase letters, reserve only BASE and HEAD. - * sanity.sh (mflag): Revert 26 Mar change; use all-uppercase tag - name again. +Thu May 8 14:21:00 1997 Larry Jones <larry.jones@sdrc.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> -Wed May 1 15:15:11 1996 Tom Jarmolowski <tjj@booklink.com> + * checkout.c (checkout_proc): Free finfo.rcs (memory leak). - * rcs.c (linevector_add): Move increment of i out of larger - statement, to avoid assumptions about evaluation order. +8 May 1997 Larry Jones <larry.jones@sdrc.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> -Tue Apr 30 15:46:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * hash.c: Add #ifdef's to disable caching. This makes it easier + to track down memory allocation problems. - * Version 1.7.87. +Thu May 8 11:40:39 1997 Larry Jones <larry.jones@sdrc.com> - * server.c (check_password): Don't use ANSI string concatenation. - Reindent function. + * sanity.sh: In setting "tests" use a number of statements rather + than one very long line. -Wed Apr 24 17:27:53 1996 Norbert Kiesel <nk@col.sw-ley.de> +Thu May 8 11:40:39 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * vers_ts.c (Version_TS): xmalloc enough space (1 more - byte). Thanks to purify! + * cvsbug.sh: Remove $Id; we decided to get rid of these some time + ago. -Fri Apr 19 11:22:35 1996 Benjamin J. Lee <benjamin@cyclic.com> +Thu May 8 11:34:02 1997 Larry Jones <larry.jones@sdrc.com> - * Version 1.7.86 + * cvsbug.sh: Put separate statements on separate lines, so it + works if awk is AT&T nawk. -Thu Apr 18 1996 Jim Kingdon <kingdon@cyclic.com> +Tue May 6 16:56:00 1997 Larry Jones <larry.jones@sdrc.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> - * client.c (try_read_from_server): Compare return value from fwrite - with a size_t not an int (Visual C++ lint). + * cvsrc.c (read_cvsrc): Fix various memory allocation problems: + rearrange code to avoid leaks, use xrealloc instead of xmalloc/ + copy/free, make sure there's room for the remaining args before + appending them. -Wed Apr 17 11:56:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Tue May 6 14:20:00 1997 Larry Jones <larry.jones@sdrc.com> - * client.c (try_read_from_server): New function. - (read_from_server): Use it. - (read_counted_file): New function. - * client.c, server.c: Add Template response. - * cvs.h (CVSADM_TEMPLATE): Added. - * logmsg.c (do_editor): If repository is NULL, use CVSADM_TEMPLATE - file in place of rcsinfo. - * server.c, server.h (server_template): New function. - * create_adm.c (Create_Admin): Call it. + * edit.c (watch_onoff, edit, unedit, editors): Add -R like + other things with -l. + * watch.c (watch_addremove, watchers): Ditto. -Tue Apr 16 13:56:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Mon May 5 18:10:37 1997 larry.jones@sdrc.com + and Jim Kingdon <kingdon@harvey.cyclic.com> - * repos.c (Name_Repository): Fix comments. - * create_adm.c (Create_Admin): Fix indentation. + * sanity.sh: Change all /tmp/cvs-sanity to TESTDIR. If TESTDIR + environment variable is set, use it instead of /tmp/cvs-sanity. + * sanity.sh: Make TMPPWD the pwd equivalent of TESTDIR, not of /tmp. -Wed Apr 10 16:46:54 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +4 May 1997 Larry jones <larry.jones@sdrc.com> + and Jim Kingdon - * options.h.in: Include relevant information here rather than - citing (former) FAQ. + * checkout.c, diff.c, patch.c, rcs.c: Update usage messages. + * rcs.c (annotate): Add -R like other things with -l. - * ChangeLog-9395: Fix typo in introductory paragraph. +Sat May 3 14:57:40 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Apr 10 14:55:10 1996 code by Mike Spengler mks@msc.edu - comments by Jim Kingdon <kingdon@harvey.cyclic.com> + * sanity.sh (basic1): Rewrite test (use dotest, unroll the loops + which IMHO makes the test a zillion times more understandable, and + only do the variant which tests for 4 files at a time--we test one + file at a time lots of places). - * filesubr.c (unlink_file_dir,deep_remove_dir): Don't call unlink - on something which might be a directory; check using isdir instead. +2 May 1997 Ziv Gigus <ziv@rest.home.net> + and Jim Kingdon -Wed Apr 10 14:55:10 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * client.c, client.h (client_process_import_file): New argument + all_files_binary means treat all files as binary. + * import.c (import_descend): Pass it if -kb is specified. + * client.c (client_process_import_file): In the + non-all_files_binary case, call wrap_rcsoption to determine + whether the file is binary. - * checkout.c (build_dirs_and_chdir): Pass path, not cp, to - Create_Admin. The former is the correct update dir. - * sanity.sh (modules): New tests modules-155* test, for above fix. +Thu May 1 13:44:51 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Mon Apr 8 13:53:27 1996 Samuel Tardieu <sam@inf.enst.fr> + * sanity.sh (binfiles2): New tests, for update -j and binary files. - * rcs.c (annotate_fileproc): If the file is not under CVS control, - return instead of dumping a core. Don't bug on files with an empty - first revision. +Wed Apr 30 11:18:36 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Fri Mar 29 16:08:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * recurse.c (start_recursion): Also free reposfile. + Don't look in repository if client_active (latter bug reported by Paul + Sanders <p.sanders@dial.pipex.com>). - * rcs.c (annotate_fileproc): If last line of add-chunk is not - newline terminated, end the loop when we find that out. +Mon Apr 28 22:36:39 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Fri Mar 29 16:59:34 1996 Norbert Kiesel <nk@col.sw-ley.de> + * diff.c (diff_file_nodiff): Remove SERVER_SUPPORT ifdefs. They + were not based on server_active, which doesn't really make any + sense (it meant that compiling --disable-server could affect the + behavior of the non-client/server CVS). This affected the output + in tests death2-diff-11 and death2-diff-12 in the testsuite. + * sanity.sh (newb-123j0): Also accept "Needs Checkout", for a + --disable-server CVS. - * rcs.c (annotate_fileproc): allow last line of add-chunk not to - be newline terminated + * main.c (cmd_usage): Change "run diffs" to "show differences"; + the former is jargon. + * edit.c (edit_usage): Fix typo ("." -> ","). + * edit.c (editors_usage), watch.c (watchers_usage): Mention -l. + * checkout.c (export_usage): Say what -P does. + * history.c (history_usg): Add comment about message wording. -Thu Mar 28 10:56:36 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Mon Apr 28 14:47:45 1997 Norbert Kiesel <nk@cosa.de> - Add more diff tests: - * sanity.sh (basic2): Use dotest for test 61. - (basica): Add test basica-6.2. - (branches): Add tests branches-14.4 and branches-14.5. - (basic1): Remove tests 19, 20, 25, and 26. The only thing this - might miss out on is diff's interaction with added and removed - files, but those tests didn't test that very well anyway. + * checkin.c (Checkin): use filename without path when calling + wrapper (bug found by Michal Schmitz <ms@cosa.de>). - * rcs.c (RCS_getrevtime): Add comment regarding years after 1999. +Fri Apr 25 13:28:55 1997 Ian Lance Taylor <ian@cygnus.com> - * rcs.c: Add "cvs annotate" command and related code. - (getrcskey): Move special handling of RCSDESC from here to - callers. Handle those keys (desc, log, text) which do not - end in a semicolon. - * rcs.h (RCSVers): Add author field. - * rcs.c (RCS_reparsercsfile): Set it. - * cvs.h (annotate), main.c (cmd_usage, cmds), client.h client.c - (client_annotate), server.c (serve_annotate, requests): Usual - machinery to add a new command. - * sanity.sh (basica): Test cvs annotate. + * client.c (update_entries): In UPDATE_ENTRIES_RCS_DIFF case, + write to a temporary file and then rename it. - * sanity.sh (branches): More tests, of things like adding files on - the trunk after a branch has been made. +Thu Apr 24 11:35:40 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Tue Mar 26 09:48:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * subr.c, cvs.h (pathname_levels): New function, from a piece of + send_file_names. + * client.c (send_file_names): Call pathname_levels in place of the + code which was moved there. + * server.c, server.h (server_pathname_check): New function. + * recurse.c (start_recursion): Call it. + * sanity.sh (modules3): New test modules3-11b tests for above fix. - * expand_path.c: Don't declare free and xmalloc; cvs.h already - takes care of that. + * filesubr.c: Do not define L_tmpnam. It is in ANSI and SunOS4, + so I don't think there will be a problem with it being missing. + Defining it too small can cause memory corruption. + (cvs_temp_name): Do not use L_tmpnam in the mktemp code; this + could cause a buffer overflow if the -T global option was in use. - * sanity.sh (mflag): Don't use tag name reserved to CVS. +Thu Apr 24 13:21:15 1997 Norbert Kiesel <nk@cosa.de> - NT local changes plus miscellaneous things noticed in the process: - * import.c (add_rcs_file): Use binary mode to write RCS file. Use - \012 where linefeed is intended. Copy data a small block at a - time, until we hit EOF, rather than trying to read the whole file - into memory at once. - * client.c (send_modified): Add comments regarding st_size. - * commit.c (commit): Add comments regarding binary mode and read(). - * logmsg.c (do_editor): Add comments regarding st_size. - * server.c (server_updated): Use binary mode to read file we are - sending. + * filesubr.c (cvs_temp_name): Use tempnam if available, else + mktemp, else tmpnam. See the comment for rationale. - * rcscmds.c (RCS_settag): Complain if user tries to add a tag name - reserved to CVS. - * sanity.sh (basica): Test for this behavior. + * sanity.sh: use "tar cf - ." instead of "tar cf - *" for + directory copies. - * sanity.sh (binfiles): New tests test ability to change keyword - expansion. +Wed Apr 23 23:41:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Mon Mar 25 1996 Jim Kingdon <kingdon@cyclic.com> + * server.c (serve_update_prog): If in readonly mode, give an error. - * cvs.h, filesubr.c (expand_wild): New function. - * recurse.c (start_recursion): Call expand_wild at beginning and - free its results at the end. - * cvs.h, subr.c (xrealloc): Make argument and return value void *. - * client.h, client.c (send_file_names): Add flags argument. If - SEND_EXPAND_WILD flag is passed, call expand_wild at beginning and - free its results at the end. - * admin.c, add.c, log.c, tag.c, status.c, edit.c, watch.c, - update.c, commit.c, remove.c, client.c, diff.c: Update callers. - -Fri Mar 22 10:09:55 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * error.c (error, fperror): Exit with status EXIT_FAILURE rather - than STATUS. We had been neglecting to check for 256, and the - value of providing a count of errors is probably minimal anyway. - * add.c, modules.c, mkmodules.c, tag.c, server.c, main.c, - import.c, client.c, scramble.c, recurse.c: Exit with status - EXIT_FAILURE rather than 1. On VMS, 1 is success, not failure. - * main.c (main): Return EXIT_FAILURE or 0. The value of providing - a count of errors is minimal. - - * client.c (init_sockaddr): Exit with status 1 rather than - EXIT_FAILURE. The latter apparently doesn't exist on SunOS4. - Reindent function. - -Mon Mar 18 14:28:00 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * cvs.h, ignore.c: New variable ign_case. - * ignore.c (ign_name): If it is set, match in a case-insensitive - fashion. - * server.c (serve_case): New function. - (requests): Add Case request. - * client.c (start_server): If FILENAMES_CASE_INSENSITIVE is - defined, send Case request. - -Sat Mar 16 08:20:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - For reference, this change takes cvs's text segment from 315392 - bytes to 311296 bytes (one 4096 byte page). - * cvs.h (struct file_info): Add fullname field. - * recurse.c (do_file_proc): Set it. - * commit.c (find_fileproc), client.c (send_fileproc), commit.c - (check_fileproc), diff.c (diff_fileproc), edit.c - (unedit_fileproc), patch.c (patch_fileproc), remove.c - (remove_fileproc), rtag.c (rtag_fileproc), tag.c (tag_fileproc), - update.c (update_fileproc), watch.c (watchers_fileproc): Use it - instead of computing it each time. - * diff.c (diff_fileproc), remove.c (remove_fileproc): Use fullname - where we had been (bogusly) omitting the directory from user - messages. - * edit.c (unedit_fileproc, edit_fileproc): If we cannot close - CVSADM_NOTIFY, mention CVSADM_NOTIFY rather than finfo->file in - error message. - * rtag.c (rtag_fileproc), tag.c (tag_fileproc): Reindent. - -Fri Mar 15 15:12:11 1996 Norbert Kiesel <nk@col.sw-ley.de> - - * server.h: fix prototype of server_pause_check (was - server_check_pause) - -Thu Mar 14 1996 Jim Kingdon <kingdon@cyclic.com> - - * vers_ts.c (Version_TS), entries.c (Scratch_Entry, AddEntryNode): - Change findnode to findnode_fn. - - * main.c: Depending on HAVE_WINSOCK_H, include winsock.h or - declare gethostname. - * cvs.h: Don't declare it here. +Wed Apr 23 19:07:41 1997 Norbert Kiesel <nk@cosa.de> -Thu Mar 14 07:06:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * subr.c (line2argv): Allocate at least 4 slots for argv. - * commit.c (find_fileproc): If vn_user is NULL and ts_user is not, - print an error rather than silently succeeding. - * sanity.sh (basica-notadded): New test, for above fix. - (dotest_internal): New function. - (dotest,dotest_fail): Call it instead of duplicating code between - these two functions. - - * sanity.sh: Skip tests binfiles-9 through binfiles-13 for remote. - - * options.h.in: Adjust comment to reflect kfogel change. - -Thu Mar 14 01:38:30 1996 Karl Fogel <kfogel@floss.red-bean.com> - - * options.h.in (AUTH_CLIENT_SUPPORT): turn on by default. + * checkout.c (checkout_proc): Add a comment which says why the + above change was necessary to avoid writing to unallocated memory. -Wed Mar 13 09:25:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Wed Apr 23 11:20:40 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * vers_ts.c (Version_TS): Don't try to override options from rcs - file if there isn't an rcs file (e.g. called from send_fileproc). - This fixes a bug detected by test 59 in "make remotecheck". + * entries.c (ParseTag): Always set *NONBRANCHP. - * rcs.c (RCS_reparsercsfile, RCS_getexpand): Assert that argument - is not NULL. +21 Apr 1997 Jim Kingdon - Fix a gcc -Wall warning: - * rcs.c, rcs.h (RCS_getexpand): New function. - * vers_ts.c (Version_TS): Call it. - * rcs.c (RCS_reparsercsfile): Make static. + * client.c (update_entries), rcs.c (expand_keywords): Rewrite + test to avoid signed/unsigned warning. - Add a "cvs init" command. This is needed because cvsinit.sh - invoked mkmodules which doesn't exist any more. - * mkmodules.c: Break filelist out of mkmodules function, rename - struct _checkout_file to struct admin_file (for namespace - correctness), and add contents field. - (init,mkdir_if_needed): New functions. - * cvs.h (init): Declare. - * main.c (cmds): Add init. - (main): If command is init, don't require cvsroot to exist. - * client.c, client.h (client_init, send_init_command): New functions. - * client.c (start_server): Don't send Root request if command is init. - * server.c (serve_init): New function. - (requests): Add "init". +Mon Apr 21 09:02:22 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Mar 13 09:51:03 MET 1996 Norbert Kiesel <nk@col.sw-ley.de> + * update.c (patch_file): Add comment about whether auto-detecting + features of the DIFF program is a good idea. - * vers_ts.c (Version_TS): set options to default option if the - file if no -k option but -A was given. This avoids the (wrong) - update message for binary files which are up-to-date when - running 'cvs -A'. +Mon Apr 21 00:03:34 1997 Ian Lance Taylor <ian@cygnus.com> - * update.c (checkout_file): remove test of -k option stored in the - file itself because it was moved to vers_ts.c + Don't require the patch program: + * client.c (struct update_entries_data): Add + UPDATE_ENTRIES_RCS_DIFF to contents enum. + (update_entries): Handle UPDATE_ENTRIES_RCS_DIFF. + (handle_rcs_diff): New static function. + (responses): Add "Rcs-diff". + * server.c (server_updated): Handle SERVER_RCS_DIFF. + (server_use_rcs_diff): New function. + * server.h (enum server_updated_arg4): Add SERVER_RCS_DIFF. + (server_use_rcs_diff): Declare. + * update.c (rcs_diff_patches): New static variable. + (update): Set rcs_diff_patches. + (update_fileproc): If rcs_diff_patches, pass SERVER_RCS_DIFF + rather than SERVER_PATCHED to server_updated. + (patch_file): Correct initial comment to say diff rather than + rcsdiff. If rcs_diff_options, pass -n to diff rather than -c. + * rcs.c (rcs_change_text): New function. + * rcs.h (rcs_change_text): Declare. - * sanity.sh: added tests for the above fix. +Mon Apr 21 00:08:59 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Tue Mar 12 13:47:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * diff.c (diff_fileproc): Add comment concerning updating the + client timestamp. - * hash.c (findnode): Adjust comment regarding errors. +Sun Apr 20 23:20:37 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * hash.c (findnode, findnode_fn): Assert that key != NULL. This - way the check still happens even if the function is later - rewritten to not start out by calling hashp. + * commit.c (commit): Add comment regarding SEND_FORCE rationale. -Mon Mar 11 10:21:05 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Sat Apr 19 17:10:36 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * sanity.sh: If expr accepts multi-line patterns but is too - liberal in matching them, print a warning but keep going. + * server.c (dirswitch): If directory ends in '/', complain. - * sanity.sh: Add QUESTION variable, analogous to PLUS. Use it - instead of \? to match a question mark. +Fri Apr 18 18:09:57 1997 Ian Lance Taylor <ian@cygnus.com> - * cvs.h (CVSMODULE_OPTS, CVSMODULE_SPEC): Move from here... - * modules.c: ...to here. They are only used here and the code to - handle the syntax of modules files should not be scattered all over. - * modules.c (CVSMODULE_OPTS): Add "+" as first character. - * sanity.sh (modules): New tests 148a0 and 148a1 test for - above-fixed bug. + * rcs.c (apply_rcs_changes): New static function, broken out of + RCS_deltas. + (RCS_deltas): Call it. + (linevector_add): Change return type to int. Return an indication + of an error for an invalid add, rather than calling error. -Mon Mar 11 13:11:04 1996 Samuel Tardieu <sam@inf.enst.fr> +Fri Apr 18 11:24:26 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * modules.c (cat_module): set optind to 0 to force getopt() to - reinitialize its internal nextchar + * version.c: Change version number to 1.9.9. -Mon Mar 11 00:09:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * version.c: Version 1.9.8. - * hash.c (findnode, findnode_fn): Revert changes of 7-8 Mar 1996. - The correct style is to assert() that key != NULL (see HACKING), - which is already done in the hashp function. - * fileattr.c (fileattr_delproc): Likewise, assert() that - node->data != NULL rather than trying to deal with it being NULL. + * commit.c (struct find_data): Add field force. + (find_fileproc, commit): Use it instead of force_ci to decide + whether to send files to server. + (commit): Set it if either -f or -r is specified. + * sanity.sh (basica): Add tests basica-8a0, basica-8a1, and + basica-8a2; tests for above fix. -Fri Mar 8 01:31:04 1996 Greg A. Woods <woods@most.weird.com> +Wed Apr 16 11:50:59 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * hash.c (findnode_fn): one more place to avoid calling hashp() - with a NULL key + * zlib.c: Remove paragraph with Free Software Foundation address. + See 2 Jan 1997 entry in ../ChangeLog for rationale. -Thu Mar 7 17:30:01 1996 Greg A. Woods <woods@most.weird.com> +Tue Apr 15 00:36:23 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (patch_file_write): Always assign to final_nl, so that + it ends up reflecting whether the data from the last call had a + newline, not whether the data from any of the calls ended in a + newline. Doesn't matter with the current RCS_checkout + implementation, but it will if RCS_checkout is changed to pass + less than the entire file. - * hash.c (findnode): also return NULL if key is not set - [[ reported by Chris_Eich@optilink.optilink.dsccc.com, and - supposedly in a PR that should be marked "fixed"..... ]] + * rcs.c (RCS_cmp_file): Change NULL to RUN_TTY in passing sout to + RCS_checkout, for clarity. - * fileattr.c (fileattr_set): set node->data to NULL after freeing - it to prevent subsequent accesses - (fileattr_delproc): don't free node->data if it's NULL, and set it - to NULL after freeing - [[ reported by Chris_Eich@optilink.optilink.dsccc.com, and - supposedly in a PR that should be marked "fixed"..... ]] + * import.c (update_rcs_file): Remove unused variable ierrno. -Fri Mar 1 14:56:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * add.c, checkout.c, commit.c, diff.c, edit.c, import.c, + history.c, log.c, main.c, patch.c, release.c, remove.c, rtag.c, + status.c, tag.c, update.c, watch.c: Pass "+" to all calls to + getopt. This ensures that we maintain existing behavior even with + glibc2. - * sanity.sh (basica): New test basica-4a tests for bug fixed by - sam@inf.enst.fr on 1 Mar 96. + * filesubr.c (fopen_case): Don't set *PATHP if we return an + error. Since the 9 Apr 1997 change, the behavior has been to + sometimes set it and sometimes not. + * rcs.c (RCS_parse): Adjust callers to not free it. Without this + change, they could call free() on an uninitialized variable. + + * checkout.c (checkout): Add comment about export -k. + + * root.c (check_root_consistent): Add comment about wording of + message. + +Mon Apr 14 11:51:49 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (call_in_directory): If rdirp reaches the end of + reposdirname, then just set it to NULL. If server does not create + directories one at a time, give a warning. + * sanity.sh (modules3): Enable tests modules3-8 through + modules3-11 for remote; tests for above fix. + + * client.c (call_in_directory): Don't set short_pathname to + pathname for a while; just use pathname itself (cleans up a relic + of the old "Repository" (not "Directory") code). Add comment + explaining short_pathname. + +Sun Apr 13 18:07:50 1997 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_checkout): Add pfn and callerdat parameters. Change + all callers. Move setting of expand after retrieval of file + data. + (struct cmp_file_data): Define. + (RCS_cmp_file): New function. + (cmp_file_buffer): New static function. + * rcs.h (RCSCHECKOUTPROC): Define type. + (RCS_checkout): Update declaration. + (RCS_cmp_file): Define. + * diff.c (diff_file_nodiff): Call RCS_cmp_file rather than + RCS_checkout and xcmp. + * import.c (update_rcs_file): Likewise. + * no_diff.c (No_Difference): Likewise. + * update.c (struct patch_file_data): Define. + (patch_file): Just return if noexec, or if binary file. Pass + patch_file_write to RCS_checkout. Don't check for newlines or + compute checksums here. Stat RCS file to set modes. + (patch_file_write): New static function. -Fri Mar 1 18:10:49 1996 Samuel Tardieu <sam@inf.enst.fr> + * update.c (patch_file): Checkout directly to file2, rather than + to finfo->file followed by rename. Remove check for whether + result of checkout is readable; that was for an old, obsolete, + form of death support. - * tag.c (check_fileproc): Check for file existence before trying - to tag it. +Sun Apr 13 13:16:40 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Fri Mar 1 07:51:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * checkout.c (build_one_dir): New function. + (struct dir_to_build): New structure. + (build_dirs_and_chir): Rewritten to accept a linked list of struct + dir_to_build rather than the silly string processing we had been + doing before. + (checkout_proc): Rewrite code that calls build_dirs_and_chdir + accordingly. + * sanity.sh: Enable tests modules3-10 and modules3-11 for local CVS; + tests for above fix. - * client.c (update_entries): If command is export, set options to - NULL. + * rcs.h (RCS_CO): Removed; no longer used. -Thu Feb 29 16:54:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Sun Apr 13 00:04:34 1997 Ian Lance Taylor <ian@cygnus.com> - * lock.c (write_lock, Reader_Lock): Remove - BOGUS_UNLESS_PROVEN_OTHERWISE code. It was pretty bogus, and has - been ifdeffed out for a long time. - * cvs.h (CVSTFL): Removed; no longer used. + Expand RCS keywords internally; never call co: + * rcs.h (struct rcsversnode): Add state field. + * rcs.c (kflags): Move out of RCS_check_kflag, and make file + static. + (enum kflag): Define. + (RCS_reparsercsfile): Always save lock information. Save state in + new state field, rather than other field. + (struct rcs_keyword): Define. + (keywords): New static variable. + (enum keyword): Define. + (printable_date, escape_keyword_value): New static functions. + (expand_keywords): New static function. + (RCS_checkout): Call expand_keywords. Don't call + RCS_exec_checkout. + (RCS_deltas): Add log and loglen parameters. Change all callers. + * log.c (log_version_requested): Use new state field. + (log_version): Likewise. + * cvs.h (RCS_exec_checkout): Don't declare. + * rcscmds.c (RCS_exec_checkout): Remove. + +Sat Apr 12 17:32:59 1997 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh (modules3): Remove second-dir at end of tests. + (sticky): Correct removal of directories at end of tests. + + * sanity.sh (keyword): New tests for RCS keyword expansion. + +Sat Apr 12 16:47:13 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (basicb): New tests basicb-1b, basicb-1c, basicb-9b, + basic-9c test current build_dirs_and_chdir behavior. + +Fri Apr 11 23:54:56 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (modules3): New tests modules3-7* test for ability to + supply a path in -d in modules. Similar to modules3-8 through + modules3-11 except because the nesting is different, these ones + work. - * cvsrc.c, cvs.h (read_cvsrc): Pass in command name rather than - using global variable command_name. - * main.c (command_name): Initialize to "", not "cvs" so that error - messages don't say "cvs cvs". Update calls to read_cvsrc to pass - in command_name or "cvs" as appropriate. - * sanity.sh (basica): New test basica-9 tests for above-fixed bug. +Thu Apr 10 00:14:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * lock.c: Rename unlock to lock_simple_remove to avoid conflict - with builtin function on QNX. + * sanity.sh (modules3): New tests modules3-12 through modules3-15 + test use of a module name which contains a slash. -Thu Feb 29 17:02:22 1996 Samuel Tardieu <sam@inf.enst.fr> + * sanity.sh (basicb): New tests basicb-14 to basicb-20 test use of + co -d with two or more arguments. - * fileattr.c (fileattr_get): Removed NULL pointer dereference - which occurred in the absence of default attribute. + * rcscmds.c: Refer to doc/RCSFILES in comment. -Thu Feb 29 07:36:57 1996 J.T. Conklin <jtc@rtl.cygnus.com> +Wed Apr 9 09:49:17 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * rcs.c (RCS_isbranch, RCS_whatbranch): Remove no longer used file - argument, swap order of remaining two arguments to be like other - RCS_* functions. - (RCS_nodeisbranch): swap order of arguments to be like other RCS_* - functions. - * rcs.h (RCS_isbranch, RCS_whatbranch, RCS_nodeisbranch): Update - prototypes for above changes. - * commit.c, rtag.c, status.c, tag.c: Update for above calling - convention changes. + * sanity.sh (basicb): New tests basicb-11 through basicb-13 test + ability to specify several directory levels in co -d (commented + out). + + * filesubr.c (fopen_case): If CVS_OPENDIR gives an + existence_error, return it to the caller instead of giving a fatal + error. + + * client.c (update_entries): Fix typo in call to error (1 -> errno). + +Tue Apr 8 23:02:22 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * error.h, error.c: Test for #ifdef __STDC__, not #if __STDC__. + This is consistent with other parts of CVS; it means that the + declaration for fperror will match the definition even if __STDC__ + is defined to 0 as the SunPro 4.0 compiler does. Reported by + Richard Smith <rjsmith@cisco.com>. + +2 Apr 1997 Jim Kingdon + + * entries.c (ParseTag): Add "break;" after "default:" to avoid + error from Visual C++. -Thu Feb 29 08:39:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Wed Apr 2 12:06:44 1997 Vince Del Vecchio <vdelvecc@spd.analog.com> + and Jim Kingdon - * client.c (start_server): Revert changes which claimed to fall - back to a different way of connecting. Add comments explaining - why. (I don't think the changes did what they claimed, anyway). - Use indentation rather than comments to line up #if, #else, and - #endif. + * client.c: In reporting errors from socket calls, use + SOCK_STRERROR and SOCK_ERRNO since strerror(errno) doesn't work + for Win32. - * patch.c (patch, patch_fileproc): Revert change to add optional - arguments to -c and -u. Optional arguments are evil and in - violation of the POSIX argument syntax guidelines. The correct - way to do this is -C and -U. Also change DIFF back to "diff" in - output (see comments). +Tue Apr 8 10:45:32 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - gcc -Wall lint: - * client.c (copy_a_file): Declare p inside the #ifdef in which is - it used. - * commit.c (remove_file): Remove unused variable p. - * commit.c (checkaddfile): Remove unused variables p. - * rcs.c (RCS_isbranch): Remove unused variable p. - * rcs.c: Remove unused declarations and definitions of - parse_rcs_proc, rcsnode_delproc, rcslist, and repository. - * rtag.c (rtag_fileproc): Remove unused variable p. - * patch.c (patch_fileproc): Remove unused variable p. - * tag.c (val_fileproc): Remove unused variable node. - * client.c, import.c, lock.c, server.c: Cast pid_t to long before - passing it to %ld. + * sanity.sh (modules3): Add tests modules3-8 to modules3-11, to + test for ability to supply a path to -d in modules. Mostly + commented out as CVS is buggy in this area. - * cvs.h: Don't prototype gethostname; merely declare it (on linux, - second argument is size_t not int). +Mon Apr 7 12:41:44 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Thu Feb 29 10:29:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de> + * add.c (add): Add comment about SEND_NO_CONTENTS. - * sanity.sh: added "cat > /dev/null" to loginfo entry to avoid the - SIGPIPE signal +Sun Apr 6 21:46:32 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Thu Feb 29 10:28:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de> + * update.c (update): Add comment about noexec and SEND_NO_CONTENTS. - * patch.c: added new variable diff_opt - (patch): allow optional parameter to -c and -u option, send it to - server - (patch_fileproc): cleaned up the code which prints the current - filename. For "-s" option, print the pathname relative to CVSROOT - instead of just the filename. +Sun Apr 6 17:34:08 1997 Robert Bihlmeyer <robbe@orcus.priv.at> - * filesubr.c (xchmod): added cast to shut up gcc + * Pass +f not f to getopt_long to prevent options from being + permuted with glibc 2.0.1. - * cvs.h: added prototype for gethostname +Sun Mar 30 00:07:05 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Thu Feb 29 10:27:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de> + * cvs.h (struct vers_ts): Adjust comment regarding ts_user. + * server.c (serve_is_modified): New function. Set entries to show + that the file is modified but we don't know the contents. + * server.c (requests): Add "Is-modified" request. + * vers_ts.c (time_stamp_server): If the timestamp in entdata is + "M" or "D", just copy that over into ts_user. + * vers_ts.c (Version_TS): If timestamp is "D", use the entries + line for the sole purpose of passing it to time_stamp_server. + * no_diff.c (No_Difference): If ts_user is "M", conclude the files + are different. + * client.h, client.c (send_files): Replace arguments build_dirs + and force with argument flags. Add flag SEND_NO_CONTENTS and add + to struct send_data. + (send_fileproc): If no_contents, then send Is-modified instead of + Modified. + * add.c, admin.c, client.c, commit.c, diff.c, edit.c, log.c, + rcs.c, remove.c, status.c, tag.c, update.c, watch.c: Change all + send_files callers. - * lock.c (write_lock), (Reader_Lock), import.c (update_rcs_file), - client.c (update_entries), (send_modified), server.c (server), - (receive_file), (server_updated): use %ld for printing pid_t - variables +Fri Mar 28 22:32:25 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Thu Feb 29 02:22:12 1996 Benjamin J. Lee <benjamin@cyclic.com> + * server.c (requests): Change "Repository" to rq_optional. I'm + not sure whether I overlooked this when I removed support for + Repository, or whether I was thinking that servers would need to + support it anyway, for CVS 1.5 to 1.9 clients, but making it + optional doesn't prevent the server from supporting it and it + seems silly for the client to complain about absence of a request + that it never will use. - * run.c (run_exec): Added VMS return status support. +Fri Mar 28 10:06:59 1997 Steven Miller <Miller@wingra.com> -Thu Feb 29 01:07:43 1996 Benjamin J. Lee <benjamin@cyclic.com> + * entries.c (Subdirs_Known): Don't create Entries.Log if noexec. - * client.c (send_to_server): wrtn wasn't being declared under - VMS for some reason. +Thu Mar 27 18:14:12 1997 Ian Lance Taylor <ian@cygnus.com> -Wed Feb 28 23:27:04 1996 Benjamin J. Lee <benjamin@cyclic.com> + * sanity.sh (death2): Remove commented out test death2-21. It + would now pass, but it duplicates the new test sticky-11. - * client.c: Changed #ifdef VMS && NO_SOCKET_TO_FD to - #if defined(VMS) && defined(NO_SOCKET_TO_FD) +Thu Mar 27 10:21:19 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Feb 28 22:28:43 1996 Benjamin J. Lee <benjamin@cyclic.com> + * sanity.sh (dotest_internal): Write test output to logfile even + if test succeeds. This was the behavior prior to 30 Sep 1996. + See the comment for rationale. - * build_src.com: Added DCL command procedure to build - and link CVS client for VMS. +Tue Mar 25 13:26:52 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Feb 28 22:07:20 1996 Benjamin J. Lee <benjamin@cyclic.com> + * cvs.h, entries.c (WriteTag): Add arguments nonbranch, + update_dir, and repository. Move the server_set_sticky call from + callers to here. + * cvs.h, create_adm.c (Create_Admin): New argument nonbranch. + * cvs.h, entries.c (ParseTag): Add argument nonbranchp. + * cvs.h (struct stickydirtag): Add field nonbranch. + * entries.c (Entries_Open): Set it. + * cvs.h (Vers_TS): Add field nonbranch. + * vers_ts.c (Version_TS): Copy it from struct stickydirtag. + * server.c, server.h (server_set_sticky): Add argument nonbranch. + * add.c, client.c, checkout.c, modules.c, update.c, create_adm.c, + commit.c: Update callers. + * add.c (add): If nonbranch, don't add the file on that "branch". + * commit.c (write_dirnonbranch): New variable. + (commit_fileproc, commit): Set it. + (commit_dirleaveproc): Pass it to WriteTag. + * update.c (rewrite_tag, nonbranch): New variables. + (update, update_dirent_proc, update_fileproc): Set them. + (update_filesdoneproc): If rewrite_tag, call WriteTag. + * sanity.sh (sticky): New tests, test for above fix. - * client.c: VMS CVS client specific changes. + * version.c: Change version number to 1.9.7. - Added USE_DIRECT_TCP to allow CVS_PORT to be used to specify - a TCP connection port (no Kerberos). Changed - start_kerberos_server() to start_tcp_server(). + * version.c: Version 1.9.6. - In copy_a_file(): transform a backup file to have a - VMS-friendly name. +Mon Mar 24 13:02:04 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - Added HAVE_CONFIG_H to include "config.h". + * entries.c (ParseTag): Add comment about unrecognized characters + in CVS/Tag file. - start_server() will starts the first successful of any - mutually exclusive methods of starting the CVS server - which might be enabled. + * classify.c (Classify_File): Add comment about how specifying a + tag (bogusly?) suppresses certain messages. - Initialized use_socket_style and server_sock for VMS in - start_server(). +Fri Mar 21 13:37:46 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Feb 28 21:49:48 1996 Benjamin J. Lee <benjamin@cyclic.com> + * rcs.h (struct rcsnode): Add comment about case of PATH. + * rcs.c (RCS_parse): If ign_case, then try opening the file with + fopen_case. + * ignore.c (ign_case): Adjust comment. + * cvs.h, filesubr.c (cvs_casecmp, fopen_case): New functions. - * find_names.c, recurse.c, cvs.h: Changed Find_Dirs() to - Find_Directories(). - * cvs.h: Added VMS filenames enabled through USE_VMS_FILENAMES - VMS POSIX will require to use the regular CVS filenames - while VMS is #define'd. +20 Mar 1997 Jim Kingdon -Wed Feb 28 21:26:22 1996 Benjamin J. Lee <benjamin@cyclic.com> + * client.c (send_repository): When sending Directory request, + send any ISDIRSEP character as '/'. Fixes + "cvs log foo\bar\baz.c" on NT & friends. - * ignore.c: Added the patterns *.olb *.exe _$* *$ to default - ignore list for VMS. + * client.c (send_file_names): Don't try to read Entries file if + CVSADM directory does not exist. Fixes fairly serious regression + (warning on all fresh checkouts) introduced by 1997-01-08 change. + +Tue Mar 18 13:03:33 1997 Jim Meyering <meyering@totoro.cyclic.com> + + * sanity.sh (RCSINIT): Define to be empty and export, to hide any + existing value that might cause spurious failures. + + * Makefile.in: (install): Depend on installdirs. + Remove `CYGNUS LOCAL' comment saying not to. + +Tue Mar 18 09:36:26 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Feb 28 13:32:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * logmsg.c (do_editor): Fix indentation. - -Wed Feb 28 12:56:49 1996 Benjamin J. Lee <benjamin@cyclic.com> - - * logmsg.c (do_editor): If no editor is defined, exit and print - a message. - -Wed Feb 28 10:40:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * vers_ts.c (time_stamp, time_stamp_server): Reindent and revise - comments. - -Tue Feb 27 23:57:55 1996 Benjamin J. Lee <benjamin@cyclic.com> - - * vers_ts.c: gmtime() returns NULL on some systems (VMS) - revert to local time via ctime() if GMT is not avaiable. - -Tue Feb 27 13:07:45 1996 J.T. Conklin <jtc@rtl.cygnus.com> - - The changes listed below cause cvs to parse each rcs file (and - free the associated rcsnode after the file has been processed) - sequentially. cvs used to parse all files in a directory, an - approach that does not scale to huge repositories with lots - of revisions/branches/tags/etc. - - * cvs.h (struct file_info): Removed srcfiles field. Added rcs - (node) field. - * recurse.c (do_recursion): Removed code that pre-parsed all - rcs files in the directory. - (do_file_proc): Parse current rcs file. - * rcs.c (RCS_parsefiles, parse_rcs_proc, RCS_addnode): Removed. - (RCS_isbranch, RCS_whatbranch): Changed srcfiles argument to - rcs (node). - * rcs.h (RCS_parsefiles, RCS_addnode): Removed prototypes. - (RCS_isbranch, RCS_whatbranch): Updated prototypes. - * add.c, admin.c, checkin.c, checkout.c, classify.c, client.c, - commit.c, diff.c, history.c, import.c, log.c, patch.c, remove.c, - rtag.c, status.c, tag.c, update.c, vers_ts: Updated for above - calling convention / data structure changes. - -Mon Feb 26 16:07:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * Version 1.7.3. - - * Version 1.7.2. - -Mon Feb 26 1996 Jim Kingdon <kingdon@cyclic.com> - - * recurse.c (start_recursion): Use last_component rather than - checking for '/' directly. - (do_dir_proc): Likewise. - - Visual C++ lint: - * client.c (send_to_server): Change wrtn to size_t. - (connect_to_pserver): Put tofd and fromfd declarations inside - #ifndef NO_SOCKET_TO_FD. - * scramble.c (shifts): Change from array of char to array of - unsigned char. - -Mon Feb 26 13:31:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * server.c (check_repository_password): Remove unused variables - linelen, ch. - - * client.c (send_file_names): Translate ISDIRSEP characters to '/'. - -Sat Feb 24 21:25:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * checkout.c (safe_location): Re-indent one line. - -Sat Feb 24 10:50:42 1996 Karl Fogel <kfogel@floss.red-bean.com> - - * checkout.c (safe_location): put assignment to hardpath[x] in an - `else'-clause, so we don't do it when x == -1. - -Sat Feb 24 01:40:28 1996 Marcus Daniels <marcus@sayre.sysc.pdx.edu> - via Karl Fogel <kfogel@floss.red-bean.com> - - * server.c (check_repository_password): Return by reference an - optional username, the `host_user', from the passwd file. The - host_user will be the user-id under which the cvs repository is - run. - (check_repository_password): Use `read_line' instead of fgets to - allow for passwords larger than 32 characters, as well as the - optional host user argument. - (check_password): Modify to use host_user. - (authenticate_connection): Modify to use host_user. - -Sat Feb 24 01:05:21 1996 Karl Fogel <kfogel@floss.red-bean.com> - - * scramble.c (descramble): just shift descrambled string to get - rid of tag char, instead of allocating a whole new copy. - (scramble): cast return value of xmalloc to avoid unsightly - compiler warnings. - - * options.h.in (RCSBIN_DFLT): don't refer to AUTH_SERVER_SUPPORT - in comment anymore, now that it's not defined in this file. - -Fri Feb 23 1996 Jim Kingdon <kingdon@cyclic.com> - - * client.c: Ifdef HAVE_WINSOCK_H, include winsock.h - instead of sys/socket.h and friends. - * login.c: Don't include sys/socket.h and friends. - * login.c (login): Only fclose fp in the case where it was - successfully fopen'd. - * login.c: Declare getpass. - * filesubr.c, cvs.h (get_homedir): New function. - * cvsrc.c, expand_path.c, history.c, login.c: Call it instead - of getenv ("HOME"). - -Fri Feb 23 09:23:20 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * client.c (connect_to_pserver): Remove unused variable host. - * login.c: Include getline.h. - (login): Remove unused variables i and username. - (get_cvs_password): Move free of linebuf to where it actually will - be called. Add a "return NULL" at the end of the function to shut - up gcc -Wall. - - * options.h.in: Remove AUTH_SERVER_SUPPORT. - * client.h (authenticate_connection): Declare. - * scramble.c (scramble): Cast char to unsigned char before using - it to look up in table (char might be signed). - * server.c [AUTH_SERVER_SUPPORT]: Include grp.h - (authenticate_connection): Remove unused variables len and - server_user. - - * sanity.sh (basica): Add comments regarding creating a top-level + * recurse.c (struct recursion_frame): Reindent. + (do_dir_proc): Print message if we try to recurse into a CVSADM directory. - (basic1): Don't try to remove first-dir and - ${CVSROOT_DIRNAME}/first-dir at start of test; tests are now - responsible for cleaning up at the end. - (PLUS,DOTSTAR,ENDANCHOR): Add comments regarding fixed GNU expr. - -Thu Feb 22 22:34:11 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * cvs.h: Remove alloca cruft. + * sanity.sh (basicb): New test basicb-4a tests for above fix. + +Sun Mar 16 10:18:28 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (death2): Replace regexp matching temporary file name + with new variable ${tempname}. For most of the tests this is a + cosmetic change, but death2-diff-6 had been missing _ which caused + it to fail on Solaris (at least sometimes). + + * sanity.sh (modes): Don't use export -n; it doesn't seem + to be sufficiently portable. + + * version.c: Change version number to 1.9.5. + + * version.c: Version 1.9.4. + + * rcscmds.c (RCS_checkin): Preserve the mode of the rcsfile. + RCS_CI usually, but not always, does this for us. + * commit.c (fix_rcs_modes): Replace algorithm with a more + CVSUMASK-friendly one. + * sanity.sh (modes): Update tests modes-5, modes-7, modes-10, and + modes-15 so they test that CVSUMASK is honored. + +Sun Mar 16 10:18:28 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (modes): New tests modes-7a and modes-7b test behavior + if one manually changes the modes in the repository. + + * server.c (server): Revise code which checks for errors creating + temporary directory. This won't solve the intermittent + can't create temporary directory + Unknown error -1 + but it will mean (a) the right message based on errno gets + printed, instead of "unknown error -1", and (b) the message says + that it happened in chmod instead of mkdir_p. + +Sat Mar 15 16:47:12 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (modes): New tests. Note that (for now) these are + just testing how CVS already behaves; I want to record that before + I move on to changing CVS's behavior with modes of RCS files. + +13 Mar 1997 Jim Kingdon + + * subr.c (line2argv): Change argv_allocated from size_t to int. -Wed Feb 21 07:30:16 1996 J.T. Conklin <jtc@rtl.cygnus.com> +Wed Mar 12 22:16:44 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * modules.c (do_module): call free_cwd before exiting. + * add.c (add_directory): If repository has an extraneous "." + directory at the end, strip it off. This fixes a bug which was + introduced when strip_path was nuked (this fix is much more + limited in scope than strip_path was; I _think_ that is a good + thing). + (add): Likewise, for client. + (combine_dir): New function, helps with above. + * sanity.sh (modules3): Reenable tests for this behavior. + (basica-0b, basicb-0e): Adjust test to reflect "foo/bar" instead + of "foo/./bar" in message. As with the rest of this, I believe + this is just restoring the behavior prior to the strip_path nuking + (I tried it with CVS 1.9). - * recurse.c: Removed entries global variable. - (do_recursion): Declare entries. Moved call to Entries_Close so - entries list is closed on all code paths. - (start_recursion): Removed call to Entries_Close, entries list has - been moved to do_recursion only. +Sun Mar 9 10:06:29 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Tue Feb 20 22:10:05 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * root.c (parse_cvsroot), server.c (serve_root, serve_init): + If CVSroot_directory is not an absolute pathname, give a fatal error. + * sanity.sh (crerepos): New tests crerepos-6* test for above fixes. - * update.c (update_dirent_proc): If dir lacks a CVS subdirectory, - don't recurse into it. - * sanity.sh (conflicts): Test for above-fixed bug. +Sat Mar 8 22:06:17 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * update.c (merge_file): Use write_letter not printf. + This cleans up the last known code which can overflow its buffers + (except the Mac client). I've skimmed through much of CVS looking + for other such places; but I didn't read everything. If I missed + any please report it to bug-cvs. + * logmsg.c (logfile_write, title_proc): Realloc str_list as + needed; don't assume MAXLISTLEN is enough. + * cvs.h (MAXLISTLEN, MAXFILEPERDIR): Removed; no longer used. + * add.c, myndbm.c, parseinfo.c, update.c: Nuke MAXLINELEN limit. + * parseinfo.c, update.c, mkmodules.c: Check for errors reading file. + * cvs.h (MAXLINELEN): Removed; no longer used. + * logmsg (MAXHOSTNAMELEN): Removed; not used. + * main.c (cmd_synonyms): Allocate based on fullname, nick1, and + nick2, just in case someone makes those big enough so that 100 + bytes is not enough. + (Make_Date): Use MAXDATELEN rather than our own fixed size. + * mkmodules.c (mkmodules): Nuke arbitrary limit on line length. + * rcs.c (ALLOCINCR): Remove; not used. + (RCS_check_kflag): Add comment concerning karg size. + * run.c: Allocate run_prog to the needed size, rather than + allocating a fixed size buffer. -Tue Feb 20 12:34:07 EST 1996: Gary Oberbrunner <garyo@avs.com> - and Jim Kingdon <kingdon@cyclic.com> +Fri Mar 7 22:39:08 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * history.c (history_write): Change username to char * and call - getcaller() to set it. Setting username accidentally got deleted - 8 Feb 96. - * sanity.sh: Revise test 64 to test for above-fixed bug. - * sanity.sh (PLUS): New variable, work around yet another GNU expr - bug. - -Tue Feb 20 14:07:50 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * logmsg.c (logfile_write): Allocate prog to needed length rather + than assuming MAXPROGLEN is enough. + * cvs.h (MAXPROGLEN): Removed; no longer used. + * subr.c (MIN_INCR): Update comment to reflect MAXPROGLEN's demise. - * sanity.sh: Merge test rtags into test basic2. They never were - capable of running separately of each other. - - * sanity.sh (deep): New test, to test ability to operate in deeply - nested directories (more quickly than basic2 test did). - (basic2,rtags): Remove directories dir3 and dir4. Remove file8, - file10, file12, file9, file11, file13, file15, file16, file17. - These additional files slowed down the tests considerably without - significantly increasing coverage. + * subr.c (free_names): Fix comment: this function is not used to + free memory allocated by Find_Names (at least it hasn't for a long + time). + * subr.c, cvs.h (line2argv): Change calling convention so that we + allocate argv array rather than the caller. The previous one had + no way of checking whether we overflowed the passed-in buffer. + * subr.c (free_names): Free the argv array too. + * modules.c (do_module, cat_module): Update callers. - * sanity.sh (PROG): New variable. Use it instead of "cvs" - to match the name cvs prints out for itself. +Thu Mar 6 12:44:42 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Mon Feb 19 09:00:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * import.c: Allocate vhead and vbranch dynamically; removes + arbitrary limit. + * history.c: Likewise (since_rev, since_tag, backto, rec_types). + * ignore.c: Likewise (line). Also check for errors from getline + and add 'copyright' notice to top of file. + * wrapper.c (wrap_add_file): Likewise (line). Also check for + errors from various calls and add 'copyright' notice to top of file. - This fixes a bug whereby old default attributes would get - clobbered instead of added to on "cvs watch add". - * hash.c (findnode): Don't check for key == NULL; let the - assertion in hashp take care of it. - * fileattr.h, fileattr.c (fileattr_get): If filename is NULL, - return default attributes. +Tue Mar 4 17:39:15 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (update_entries): Add comment about "move away <file>" + message. - * client.c (send_repository): Fix indentation. +Mon Mar 3 21:51:40 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Mon Feb 19 01:10:01 1996 Karl Fogel <kfogel@floss.red-bean.com> + * sanity.sh (basicb): Clean up topfile,v at end of test. Fixes + failure in modules-155b. + +Sun Mar 2 18:11:09 1997 Dan Wilder <dan@gasboy.com> + and Jim Kingdon + + * admin.c (admin): Arrange to perform recursion if "cvs admin" + is passed only options. + +Sun Mar 2 18:11:09 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * login.c (login): print out full repos so user knows which server - she's logging into. + * sanity.sh (basicb): New tests basicb-0* test for files at top + level. - * client.c (send_repository): die if `repos' is NULL. This is a - lame solution; see comments in code. + * error.c (error): Add newline to "out of memory" message. I think + that its omission probably could cause the message to be lost in + the bowels of server.c and never passed to the user. -Thu Feb 15 15:04:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * client.c (start_rsh_server): Add comment about "remsh" vs. "rsh". + + * cvs.h: Move copyright notice to top of file. + +Sun Mar 2 13:44:36 1997 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh: Use -n when testing whether rsh works. + + * server.c (serve_root): Free path. + +Sun Mar 2 13:12:46 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + The following are things that I noticed in the process of trying + to track down: + can't create temporary directory + Unknown error -1 + FAIL: test 28 + from nightly testing. I'm not sure that either item explains that + message however. + * server.c (server): Allocate pending_error_text; + print_pending_error will try to free it so + pending_error_text = "foo" + won't work. + (mkdir_p): Don't assume that isdir will leave errno unmolested. + +Thu Feb 27 15:29:58 1997 Ian Lance Taylor <ian@cygnus.com> + + * remove.c (cvsremove): When forcing removal in client mode, use + start_recursion rather than calling CVS_UNLINK on each argument. + (remove_force_fileproc): New static function. + * sanity.sh (deep): Add tests deep-rm7 through deep-rm10 for above + patch. + + * sanity.sh (death): Enable death-76a0 and death-76a1 tests for + remote, since they now work. + +Wed Feb 26 16:13:26 1997 Ian Lance Taylor <ian@cygnus.com> - * error.c (error): Free entire and mess when done with them. + * client.c (add_prune_candidate): Skip adding this directory if + it is the same as the first directory already on the list. - * sanity.sh (info): Correct syntax of .cvsrc file. +Mon Feb 24 21:36:43 1997 Noel Cragg <noel@gargle.rain.org> - * cvs.h, expand_path.c, edit.c, parseinfo.c, wrapper.c: - expand_path now takes arguments containing file and line for error - message, and it prints the error message itself. - * sanity.sh (info-6a): Test printing of error message. - - * expand_path.c (expand_variable): Add USER internal variable. - * sanity.sh (info): Test USER and CVSROOT internal variables too. + * main.c (lookup_command_attribute): Add the "init" command to the + list of commands that don't use the current working directory. -Wed Feb 14 19:11:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Sun Feb 23 09:54:49 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * main.c (usg): Add -s option. - -Tue Feb 13 20:26:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - gcc -Wall lint: - * mkmodules.c (mkmodules_usage): Remove declaration of - non-existent function. - * cvs.h (mkmodules): Declare. - -Mon Feb 12 12:20:04 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * mkmodules.c: Rename main to mkmodules and remove various pieces - of scaffolding which it used to emulate non-existent parts of CVS. - Change calling convention to just take a char * not argc,argv. - Save and restore working directory. - * commit.c (commit_filesdoneproc): Call it if checking files into - CVSROOT. - * Makefile.in (SOURCES): Add mkmodules.c. - (OBJECTS): Add mkmodules.o. - (MSOURCES,MOBJECTS): Removed. - (COMMON_OBJECTS): Removed; move former contents into OBJECTS. - Update other rules accordingly. - * sanity.sh: Adjust to reflect nonexistence of mkmodules. - - These changes introduce functions cvs_output and cvs_outerr; - eventually all server output will go through them rather than - stdio directly. - * server.c (saved_output, saved_outerr): New variables. - (do_cvs_command): Initialize them. - (buf_output): Don't require that buf->output be set; saved_* use - this to shove some data in a buffer which buf_copy_lines will - later want to get data from. - * server.c, cvs.h (cvs_output, cvs_outerr): New functions. - * mkmodules.c (cvs_outerr): New function, so error() works. - * error.c: Reindent. Don't declare program_name and command_name; - cvs.h declares them. - (error): Use vasprintf and cvs_outerr (or fputs in the - error_use_protocol case) rather than stdio directly. - * import.c (import_descend_dir): Remove kludge which had prevented - messages from error() from being out of order with respect to - messages from printf; cvs_output and cvs_outerr are a cleaner - solution to the problem. - (add_log, import): Use cvs_output not printf. - * update.c (write_letter): Use cvs_output not printf. - (checkout_file): Use write_letter not printf. - * sanity.sh: Use dotest for test 56 (test that output is actually - correct). In theory should test that the import.c bug is fixed, - but I was unable to reproduce the bug (it is timing dependent). - -Mon Feb 12 16:07:45 1996 Norbert Kiesel <nk@col.sw-ley.de> - - * commit.c: define last_register_time - (commit): make sure cvs doesn't exit in the same second it wrote - the last timestamp - (commit_fileproc): set last_register_time - (finaladd): set last_register_time - - * run.c, cvs.h: Changed more Popen() to run_popen() - -Mon Feb 12 03:06:50 1996 Benjamin J. Lee <benjamin@cyclic.com> - - * release.c, rtag.c, tag.c: changed 'delete' to 'delete_flag' - to avoid symbol collision with DEC C RTL function delete() - -Mon Feb 12 03:01:48 1996 Benjamin J. Lee <benjamin@cyclic.com> - - * mkmodules.c: changed 'void Lock_Cleanup()' to 'void static - Lock_Cleanup() to avoid conflict with more substantial - Lock_Cleanup() in lock.c - -Mon Feb 12 02:50:19 1996 Benjamin J. Lee <benjamin@cyclic.com> - - * edit.c, logmsg.c, release.c, run.c: Changed Popen() to - run_popen(). VMS' linker is not case sensitive and considered - popen() and Popen() to be identical symbols. - -Sun Feb 11 10:51:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * main.c (main) [!CLIENT_SUPPORT]: Silently ignore gzip level - rather than printing usage message. - - * cvs.h, expand_path.c (variable_list): New variable. - (variable_set): New function. - * hash.h (enum ntype), hash.c (nodetypestring): Add VARIABLE. - * expand_path.c (expand_path, expand_variable): Reindent. - (expand_variable): Use user variables not environment variables - for ${=VAR} syntax. The environment variables didn't work - client/server. - * main.c (main): Process new -s global option. - * client.c (send_variable_proc): New function. - (start_server): Call it, to send user variables. - * server.c (serve_set): New function. - (requests): Add Set request. - * sanity.sh: Revise info test to use user variables rather than - environment variables. - -Sat Feb 10 16:55:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - By itself this is only a small cleanup, but in the long run it - will be way cool (for reference, it takes CVS's text segment from - 290816 bytes to 294912, which I expect will be made up by future - changes which this enables): - * cvs.h (struct file_info): Added. - (FILEPROC): Replace 5 args with single struct file_info *. - * recurse.c (do_file_proc): Adjust args to fileproc; passed in - instead of from globals. - (do_recursion): Call do_file_proc accordingly. Remove srcfiles - global variable. - * update.c (update_fileproc): Renamed from update_file_proc. - * admin.c, client.c, commit.c, diff.c, edit.c, log.c, patch.c, - remove.c, rtag.c, status.c, tag.c, update.c, watch.c: Update - fileprocs to new calling convention. - -Fri Feb 9 15:30:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * expand_path.c (expand_variable): Accept a variable name starting - with '=' as a way to specify an environment variable. - * sanity.sh (info): New tests, for above behavior. - - * Makefile.in (clean): Also remove check.log check.plog. - - * import.c (comtable): Remove SYSTEM_COMMENT_TABLE; the table - should *not* depend on what kind of machine the server happens to - be. Add "mak", "rc", "dlg", "frm", and "bas" types which were - formerly included via SYSTEM_COMMENT_TABLE. - - * cvs.h, rcs.h, add.c, checkin.c, classify.c, commit.c, diff.c, - import.c, patch.c, rcs.c, update.c, vers_ts.c: Remove - DEATH_SUPPORT ifdefs. They were introduced to facilitate merging - between Cygnus and Berliner variants of CVS, not because it was - intended to subset CVS this way. And they clutter up the code - quite a bit. - * cvs.h, create_adm.c, main.c, update.c: Likewise, remove - CVSADM_ROOT ifdefs (it is still a #define, of course). I believe - they had a more-or-less similar motivation. - - * sanity.sh: Move setting of HOME from ignore test to the start of - the tests so it applies to all tests. - (CVS): Remove -f; the above change takes care of it. - - * rcs.h (RCS_MERGE): Removed; unused. - - * commit.c (checkaddfile): Fix memory leak. - - * admin.c, commit.c, diff.c, log.c, mkmodules.c: Pass -x,v/ to RCS - commands. - - * rcscmds.c, cvs.h (RCS_checkin): New function. - * checkin.c, commit.c, import.c: Call it, rather than run_*. - * cvs.h, commit.c: Remove DEATH_STATE define; the behavior - which used to be the default (DEATH_STATE) is now the only one. - Failing to define DEATH_STATE has been commented as obsolete at - least since CVS 1.5. We still can read repositories created with - such a CVS, however. - * rcs.h, rcs.c: Adjust comments regarding DEATH_STATE. - * subr.c (make_message_rcslegal): Add comment, describing - allocation of returned value. - -Fri Feb 9 09:53:44 MET 1996 Norbert Kiesel <nk@col.sw-ley.de> - - * sanity.sh: use "${testcvs}" instead of "cvs" in devcom tests - - * hash.c: fix "dereferencing a NULL pointer" bug triggered with - "cvs watch add" - (findnode): return NULL if key == NULL - (hashp): assert (key != NULL) - -Fri Feb 9 00:46:47 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * rcs.c (RCS_reparsercsfile): Remove unused variable date. - - * myndbm.c (mydbm_load_file): Fix typo ('015' -> '\015'). - -Thu Feb 8 13:00:00 1996 Jim Kingdon <kingdon@peary.cyclic.com> - - * rcs.c (RCS_parse, RCS_parsercsfile, RCS_reparsercsfile), - fileattr.c (fileattr_read), myndbm.c (myndbm_open): - Use FOPEN_BINARY_READ. - * fileattr.c (fileattr_write), myndbm.c (myndbm_close): - Use FOPEN_BINARY_WRITE. - * history.c (history_write, read_hrecs): Specify OPEN_BINARY. - * rcs.c: Remove calls to abort. - * myndbm.c (myndbm_load_file): Ignore CRs from ends of lines - if present. - * myndbm.c, fileattr.c: While I am at it, change \n to \012 - a few places where LF is intended. - * history.c (history_write): Use getenv ("HOME"), not getpwnam, - to find home directory. If it isn't set, just keep going; don't - print a message. - * rcscmds.c, cvs.h (RCS_checkout): New function. - * update.c, checkin.c, commit.c, diff.c, import.c, no_diff.c, - patch.c: Call it instead of run_*. - * patch.c (patch_fileproc): Clean up inconsistent handling of - noexec flag. - * rcscmds.c (RCS_*): Pass -x,v/ to RCS commands; elsewhere in - CVS it is assumed that ,v is a suffix. - -Fri Feb 2 14:07:32 1996 J.T. Conklin <jtc@rtl.cygnus.com> - - * rcs.h (struct rcsnode): Remove dates field (list of rcsversnodes - indexed by date). CVS maintained this list for each RCS file even - though it was never used. This resulted in higher then necessary - memory requirements (and run time too). Even if revision info was - needed, CVS' List data structure is inappropriate because can't - handle duplicate keys. The above was discovered by tracking down - a memory leak. - * rcs.c (RCS_reparsercsfile): Don't build dates list. - (freercsnode): Don't delete dates list. - (rcsvers_delproc): Free date field. - (null_delproc): Removed. - -Thu Feb 1 12:28:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * remove.c (cvsremove): Don't tell user the name of the program - which they use to remove files; we don't have any way of knowing - that, and besides which they might use a GUI or emacs 'dired' anyway. - * update.c (update_filesdone_proc, update_dirleave_proc): Call - unlink_file_dir instead of rm -rf. - * options.h.in: Remove RM; no longer used. - - * sanity.sh: New tests devcom-a* test "cvs watch add", - "cvs watch remove", and "cvs watchers". - - * sanity.sh: New test 171a0 tests for watch.c bug just fixed by kfogel. - - * Most .c files: Remove rcsids. - * cvs.h: Remove USE macro. - -Thu Feb 1 13:07:15 1996 J.T. Conklin <jtc@rtl.cygnus.com> - - * tag.c, rtag.c: Update various comments to reflect function name - changes. - -Thu Feb 1 14:14:31 1996 Karl Fogel <kfogel@floss.red-bean.com> + * sanity.sh (devcom3): Clean up at end of test. - * recurse.c (do_recursion): comment #endif. + * sanity.sh (basicb): Add commented out test basicb-8a0, for + whether CVS can print an error on bad numeric revision to diff. + Commented out until we get around to fixing CVS. + * diff.c (diff_file_nodiff): Add comment about this case. - * edit.c (notify_check): surround with #ifdef CLIENT_SUPPORT; else - CVS won't compile if CLIENT_SUPPORT is undefined. - - * edit.h (notify_check): surround declaration with #ifdef - CLIENT_SUPPORT. - - * watch.c (watch): if argc <= 1, then just give usage (previously - was "argc == -1"). - -Thu Feb 1 12:28:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * README-rm-add: Remove information which is now in cvs.texinfo. + * fileattr.c (fileattr_read): If a filename is duplicated, + continue to ignore subsequent lines but free the node so that we + don't leak memory. + * sanity.sh (devcom3): New tests devcom3-8 and devcom3-9 test for + behavior on duplicated filenames. - * sanity.sh: Remove basic0 tests. Move a few to new tests - basica-1a* (but there is no need to test that *every* command - gracefully does nothing on an empty directory; exhaustive testing - is impractical and the generic recursion processor handles this - anyway). + * fileattr.h: Add comment about unrecognized ENT-TYPE and order of + lines in fileattr file. + * fileattr.c (struct unrecog, unrecog_head): New variables, to + record unrecognized lines. + (fileattr_startdir): Assert that unrecog_head == NULL. + (fileattr_read): Record unrecognized lines in unrecog_head linked + list rather than ignoring them. + (fileattr_write): Also write out unrecognized lines, if any. + * sanity.sh (devcom3): New tests, test for above fix. - * sanity.sh: New tests 69a* test use of update -p to restore old - version of dead file. + * fileattr.h (fileattr_modify): Fix example in comment. -Wed Jan 31 18:32:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Sat Feb 22 08:30:27 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * ChangeLog-9395: Remove duplicate entries from 1996 which - accidentally got into this file. + * sanity.sh: Add variable username. + (basica rdiff multibranch log log2): Use it instead of our own + (inconsistent) ways of matching an author name. - * client.c (read_line, read_from_server): Change "premature end of - file from server" message to "end of file from server (consult - above messages if any)" because 99% of the time it means rsh has - printed an error message and exited. + * filesubr.c, root.c, rtag.c, server.c, subr.c, update.c, + wrapper.c: Nuke PATH_MAX. + * cvs.h, wrapper.c (wrap_fromcvs_process_file): Now returns void + (return value had been unused). + * cvs.h: Adjust comment to reflect the fact that PATH_MAX is + gone, at least from src/*.c (except safe_location, as noted). -Wed Jan 31 15:09:51 1996 J.T. Conklin <jtc@rtl.cygnus.com> +22 Feb 1997 patch by Tom Hageman <tom@basil.icce.rug.nl> (4 Jun 1996) + updated and commented by Jim Kingdon <kingdon@harvey.cyclic.com> - * edit.c (ncheck_fileproc): Fix memory leak; free line before - returning. + * update.c (checkout_file): Call unlink_file_dir on backup, not + unlink_file. -Tue Jan 30 18:06:12 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Fri Feb 21 16:40:03 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * recurse.c (do_recursion): Add comment about the fact that we - don't have locks in place at certain points. + * Makefile.in (DISTFILES): Remove NOTES. -Tue Jan 30 09:43:34 1996 Vince Demarco <vdemarco@bou.shl.com> + * NOTES: Removed. bcopy->memcpy is done. "static buffers" I + assume refers to what is covered by reentrancy text in HACKING. + Obstack idea moved to comment in hash.c (at nodecache). Checking + system calls for error returns largely done, and isn't a very + helpful suggestion unless you know where the bogus calls are + anyway. Sizing limits--we're in the progress of removing them + (assuming it meant things like PATH_MAX and earlier, already + nuked, limits). Removed various items about changes which were + done a long time ago (I realize that the ChangeLog's probably + aren't reliable that far back, but I'm not convinced anyone cares + anymore). CONFIRM_DIRECTORY_ADDS: I assume this is a + reference to the #if 0'd code in add_directory which asks for + confirmation--a better way of making it harder to accidentally add + directories would be to have to add and commit directories like + for files. I don't know what FORCE_MESSAGE_ON_ADD meant. + + * rcs.c (RCS_getrevtime): Fix documentation (in particular, the + size of the array that DATE must point to, but many other things + too). + * patch.c, recurse.c, release.c, remove.c, repos.c: Nuke PATH_MAX. + (patch_fileproc): Use MAXDATELEN not hardcoded 50. + +Sun Feb 16 12:00:44 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (client_process_import_file): New variable fullname; + pass it to send_modified. This finishes the job of untangling the + old short_pathname variable into update_dir vs. fullname. - * edit.c (notify_proc): have notify_proc call expand_path with - the name of the filter program. The user may have used a - cvs environmental variable. (Popen will expand it, but it may not - use the correct value) + * client.c (client_process_import_file): Nuke first_time. If + toplevel_repos were ever NULL here, the code would dump core in + strncmp a few lines down. And client_import_setup ensures + toplevel_repos is not NULL. + +Sun Feb 16 08:16:48 1997 Ian Lance Taylor <ian@cygnus.com> -Tue Jan 30 09:43:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * client.c (client_process_import_file): Rename short_pathname to + update_dir (to reflect its function) and make sure that it doesn't + point to uninitialized memory if repository and toplevel_repos + contain the same string. - * ChangeLog: take the pre-1996 changes and put them in a new file - ChangeLog-9395. - * ChangeLog-9194: Renamed from ChangeLog.fsf. - * ChangeLog-9194, ChangeLog-9395, ChangeLog: Add additional text - explaining the difference between all these logs and pointing to - older logs. - * Makefile.in (DISTFILES): Add ChangeLog-9194 and ChangeLog-9395; - remove ChangeLog.fsf. +Sun Feb 16 08:16:48 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * modules.c (do_module): Don't fall through from 'l' to 'o' case - of option processing switch statement. - -Tue Jan 30 06:50:19 1996 J.T. Conklin <jtc@rtl.cygnus.com> - - * client.c (send_repository): Fix memory leak; free adm_name - before returning. - * diff.c (diff_file_nodiff): Fix memory leak; free xvers before - returning. - * rtag.c (rtag_fileproc): Fix memory leak; if branch_mode is set, - free rev before returning. - * status.c (status_fileproc, tag_list_proc): Fix memory leak; free - return value of RCS_whatbranch. - * tag.c (tag_fileproc): Fix memory leak; free vers before - returning. - (val_fileproc): Fix memory leak; free return value of RCS_gettag. - * watch.c (watch_modify_watchers): Fix memory leak; free mynewattr - before returning. - -Tue Jan 30 09:43:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * lock.c (readers_exist): If stat gave an error, print an error - message saying it was from stat, rather than from "reading - directory". Skip the message completely if it was an - existence_error. - - * sanity.sh (branches): New tests (branches off of branches, etc.). + * client.c (start_rsh_server): Nuke comment about weirdnesses with + pre-1.5 versions of CVS and .bashrc/.cshrc. The remote protocol + is interoperable only back to 1.5, and people who need to know are + unlikely to see this comment anyway. + +Sun Dec 15 13:12:30 1996 Michael Douglass <mikedoug@texas.net> + and Jim Kingdon <kingdon@harvey.cyclic.com> -Tue Jan 30 11:55:34 MET 1996 Norbert Kiesel <nk@col.sw-ley.de> + * main.c (cmds): Added an entry for new logout command. + (cmd_usage): Added an entry for new logout command. + (lookup_command_attribute): Added 'logout' to list of commands + that set need_to_crate_root to 1. + * login.c, cvs.h (logout): New command for removing entries from + the .cvspass file. + (logout_usage): Usage information on the logout command. - * main.c (main): Add change to run getopt_long twice again. +Wed Feb 12 11:19:42 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Mon Jan 29 15:59:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * client.c (struct send_data): Fix indentation. - gcc -Wall lint: - * client.c: Include edit.h +Wed Feb 12 08:48:04 1997 Greg A. Woods <woods@most.weird.com> -Sun Jan 28 09:45:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * mkmodules.c (loginfo_contents): add missing comma in + initializer statement (caused syntax error on SunOS-4). - * edit.c, edit.h (mark_up_to_date): New function, to remove file - in CVS/Base. - * client.c (update_entries): Call it if file is up to date. - * checkin.c (Checkin): Call it in non-server (local) case. - * sanity.sh: New test 182.5, tests for above-fixed bug. +Tue Feb 11 21:14:28 1997 Ian Lance Taylor <ian@cygnus.com> -Sun Jan 28 01:07:22 1996 Jim Kingdon (kingdon@beezley) + * commit.c (find_fileproc): If force_ci is set, set the status to + T_MODIFIED even if the file hasn't changed. + (commit): Pass force_ci to send_files as new force argument. + * client.c (struct send_data): Define. + (send_fileproc): The callerdat parameter now points to a send_data + struct. If force is set, always call send_modified. + (send_dirent_proc): The callerdat parameter now points to a + send_data struct. + (send_files): Add force parameter. Change all callers. Set up a + send_data struct and pass it to start_recursion as callerdat. + * client.h (send_files): Update declaration. + * sanity.sh (basica): Add a simple test for the above patch. - * client.c (change_mode): Separate out CHMOD_BROKEN code to parse - mode_string, rather than going through a mode_t. Cleaner than - the previous CHMOD_BROKEN code (which also had a typo of && not &). +Sun Feb 9 12:58:59 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Sat Jan 27 23:29:46 1996 Jim Kingdon (kingdon@beezley) + * tag.c (cvstag), rtag.c (rtag): Pass -f to server if specified in + the client. I haven't tried to come up with a test case because + the fix seems obvious. - * edit.c (edit_fileproc): Check for EACCESS as well as EEXIST. + * import.c (add_rcs_file): Change size of altdate1 and altdate2 to + MAXDATELEN. + * cvs.h (MAXDATELEN): Fix comments; describe what this is for. -Sat Jan 27 16:26:30 1996 Karl Fogel (kfogel@floss.cyclic.com) + * diff.c (diff_usage): Document --ifdef and try to briefly say + what "rcsdiff-options" means. - * client.c (notified_a_file): use rename_file() instead of - rename() (but temporarily set `noexec' to 0 so it runs - unconditionally). - (change_mode): deal with CHMOD_BROKEN. + * update.c (update): If update had a nonzero status and we haven't + yet tried to fetch unpatchable files, go ahead and try it again. + The previous behavior was to quit, which meant that updates would + keep failing until you hacked around the problem. Patch and bug + report by Ian; comment, ChangeLog entry, and willingness to take + the flak if checking it is premature by Jim. -Fri Jan 26 00:14:00 1996 Karl Fogel <kfogel@floss.red-bean.com> + * server.c (alloc_pending): New function. + * server.c: Call it. Fixes places where we had neglected to + check for NULL return from malloc. - * server.c: renamed `dirname' to `dir_name', to avoid conflicts - with system headers. + * sanity.sh (binwrap): Add test binwrap-0, tests for import.c fix + below. - * client.c: renamed `dirname' and `last_dirname' to `dir_name' and - last_dir_name' (see above). Not strictly necessary, but - consistency is nice -- as long as you do it all the time. +Sun, 9 Feb 1997 (submitted 19 Jul 1996) John Polstra <jdp@polstra.com> -Thu Jan 25 00:41:59 1996 Karl Fogel <kfogel@floss.red-bean.com> + * import.c (import): Give error if the same tag is specified more + than once. The previous behavior was to write an RCS file which + had the same tag listed twice, once pointing to each revision, + which is not legal. - * options.h.in (AUTH_SERVER_SUPPORT, AUTH_CLIENT_SUPPORT): change - comment now that no longer under construction. +Sun Feb 9 12:37:09 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Jan 24 15:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * checkin.c (Checkin): Use cvs_output to print message (should + make out of order bugs no worse, as it merely substitues a + protocol_pipe vs. stderr_pipe race instead of a stdout_pipe + vs. stderr_pipe race). Add comment about stdout vs. stderr. - * Version 1.7.1. +Fri Feb 7 08:29:52 1997 Josef Nelissen <josef.nelissen@munich.ixos.de> - * Version 1.7. + * server.c (check_command_legal_p): Don't use ANSI-style definition. -Sat Jan 20 00:05:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Thu Feb 6 10:55:37 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * Version 1.6.87. + * patch.c (patch): Give a fatal error for -V option (see comment + for rationale). -Mon Jan 15 18:14:55 1996 Gary Oberbrunner <garyo@avs.com> - and Jim Kingdon <kingdon@harvey.cyclic.com> + * diff.c (diff): Also send "options" to server. Pretty much the + patch submitted independently by josef.nelissen@munich.ixos.de and + Ronald Khoo <ronald@demon.net>. - * tag.c (val_direntproc): New function to ignore - nonexistent dirs when recursing to check tag validity. - (tag_check_valid): Pass it to start_recursion. - * sanity.sh (death): New tests 65a0-65a6 cause test 74 to test for - above-fixed bug. +Wed Feb 5 18:57:14 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Mon Jan 15 12:55:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * modules.c (do_module): Fix typo in 30 Jan 97 PATH_MAX nuking + (free -> free_cwd). Testsuite test 151 gets credit for catching + this one. - * main.c: Revert change to run getopt_long twice. This can go in - after 1.7. +Mon Feb 3 16:14:54 1997 Ian Lance Taylor <ian@cygnus.com> -Mon Jan 15 13:03:28 1996 Norbert Kiesel <nk@col.sw-ley.de> + * main.c (lookup_command_attribute): Don't use an ANSI prototype + when defining the function. - * filesubr.c (deep_remove_dir): added test of EEXIST for nonempty - directory (Posix states that both ENOTEMPTY (BSD) and EEXIST - (SYSV) are valid) +Fri Jan 31 12:49:02 1997 Ian Lance Taylor <ian@cygnus.com> - * main.c (main): run getopt_long twice to allow command-line - suppression of reading the cvsrc file + * modules.c (do_module): Actually goto found if is_found is set + (fixes thinko in PATH_MAX nuking of 30 Jan 97). -Fri Jan 12 10:02:43 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Fri Jan 31 12:49:02 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * Version 1.6.86. + * sanity.sh: Add modules3 and big to list of tests to run + by default; they were omitted by accident. -Thu Jan 11 23:28:05 1996 J.T. Conklin <jtc@rtl.cygnus.com> - and Jim Kingdon <kingdon@harvey.cyclic.com> +Thu Jan 30 11:46:33 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * fileattr.h (fileattr_startdir): Add comment about REPOS == NULL. - * fileattr.c (fileattr_read, fileattr_write): Assert that - fileattr_stored_repos != NULL. - (fileattr_free): If fileattr_stored_repos is NULL, don't free it. + * logmsg.c, main.c, mkmodules.c, modules.c, parseinfo.c, patch.c: + Nuke more PATH_MAX. -Thu Jan 11 18:03:21 1996 Karl Fogel <kfogel@floss.red-bean.com> + * server.c (server_updated): After we send Created or + Update-existing for a file, mark it as unchanged, in case we + process it again. + * sanity.sh (modules3): New tests, test for above fix. - * scramble.c (descramble): deal with DIAGNOSTIC better. + * logmsg.c (do_verify): Error return from fopen is NULL, not -1. + Pass errno to error(). -Thu Jan 11 12:04:42 1996 Norbert Kiesel <nk@col.sw-ley.de> + * login.c [_CRAY]: Don't declare getpass. - * main.c: remove CVS_NOADMIN. +Mon Jan 27 17:25:27 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * options.h.in: remove CVS_NOADMIN + * import.c (process_import_file): Fix freeing of rcs (Don't free + it before we are done using it, and don't free it twice). -Thu Jan 11 10:28:44 1996 Karl Fogel <kfogel@floss.red-bean.com> + * modules.c (cat_module): Allocate line right before we use + it. The previous code was wrong because the length of the + s_h->rest changes between the time we allocate line and the time we + sprintf s_h->rest into it. - * scramble.c (descramble): make sure the string returned is safe - to free(). +Sun Jan 26 21:58:16 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Jan 10 01:11:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * expand_path.c (expand_path): Revise to call expand_string as + needed. Nuke PATH_MAX. + * find_names.c (find_dirs): Likewise. + * import.c, lock.c: Nuke more PATH_MAX. - * server.c (serve_notify): Cast return value from malloc. + * server.c (mkdir_p): Set retval to 0 at start of function. + Previously it had been uninitialized for some cases. Thanks are + due to nightly testing for catching this one. - * edit.c (notify_do): Use struct assignment, not struct - initialization (which SunOS4 /bin/cc doesn't have). +Sat Jan 25 21:34:19 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * subr.c, cvs.h (expand_string): New function. + * rcs.c (getrcskey, getrcsrev): Call it. This greatly reduces the + number of calls to realloc if there is a very large file in the + RCS file. Credit goes to Mike Heath <mike@pswtech.com> for + pointing out the problem and the basic solution (MIN_INCR, + MAX_INCR); I adapted it into the separate function expand_string. + * sanity.sh (big): New test helps insure this hasn't broken + anything obvious. - * Version 1.6.85. +Wed Jan 22 10:06:13 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - We use version numbers instead of patchlevels. But there was some - confusing patchlevel stuff lying around. Nuke it: - * Makefile.in (HEADERS): Remove patchlevel.h - * patchlevel.h: Removed. - * main.c: Don't include patchlevel.h. - (main): Don't print patch level. + * status.c (status_fileproc): Change message which is printed for + T_MODIFIED and ts_conflict set, so that it doesn't say "unresolved + confict". This message occurs whether the conflict is resolved or + not. + * sanity.sh (conflicts): Add tests conflicts-status-* to test + output of "cvs status" in the context of conflicts. Tests for + above fix. - * server.c (check_repository_password): Check for errors from - system calls; reindent function. + * rtag.c (rtag): Send -n if run_module_prog is NOT true. -Tue Jan 9 23:15:30 1996 Karl Fogel <kfogel@floss.red-bean.com> +Thu Jan 16 00:06:00 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * expand_path.c: fix comments (explain expand_path()'s behavior - correctly). + * version.c: Change version number to 1.9.3. -Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * version.c: Version 1.9.2. - * edit.c (notify_proc): After copying in string following %s, - don't clobber it. Instead set up q to end of string. +Wed Jan 15 09:14:38 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * watch.c (watch_modify_watchers), edit.c (editor_set): Fix sense - of test in trying to decide whether attributes are changed. + * client.c (call_in_directory): Take code that creates CVSADM at + top level, move it before the CVS_CHDIR (dir_name) call, and do it + regardless of whether dir_name is ".". Pass "." not dir_name to + Create_Admin (when the code was written they were always the + same). Don't add reposdirname to the repository we pass to + Create_Admin (when the code was written, I think reposdirname + probably would always be "."). Don't create CVSADM if + reposdirname_absolute. + * sanity.sh (basicb): Enable tests basicb-1a and basicb-9a for + remote; tests for above fix. + (basic1): Do entire test within a "1" directory to deal with + creation of CVS directories at top level. Support --keep. + (conflicts): In test conflicts-136, only update first-dir. + (basica): Uncomment the part that tests "cvs co -l .". That tests + the existing functionality which I might have (but hopefully did not) + perturbed with the call_in_directory changes. - * cvs.h (CVSROOTADM_USERS): New macro. - * edit.c (notify_do): Look up notifyee in CVSROOTADM_USERS if it - exists. +Mon Jan 13 11:04:32 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Tue Jan 9 21:39:45 1996 Karl Fogel <kfogel@floss.red-bean.com> + * server.c (check_command_legal_p): Do not call error (1, ...) + here; that will always cause a protocol violation by shutting down + the connection prematurely. Remove croak_on_illegal arg. + (do_cvs_command): Move call to check_command_legal_p until after + the call to print_pending_error. Print the error message ourself. - * expand_path.c: don't redundantly #include things that cvs.h - already #includes (i.e., stdio.h, ctype.h, string[s].h). + * mkmodules.c (filelist): Add readers and writers. Add comment + about why passwd is not included. Add comment about meaning of + NULL contents field. -Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> +Fri Jan 10 13:23:09 1997 Norbert Kiesel <nk@col.sw-ley.de> - * ignore.c (ign_default): Add *.obj. + * release.c (release): Initialize delete_flag before reading it + (found by running purify) - * server.c: Put /* */ around #endif comment. + * logmsg.c (do_verify): Fix reading unallocated memory (found by + running purify) -Mon Jan 8 20:37:17 1996 Karl Fogel <kfogel@floss.red-bean.com> +Thu Jan 9 16:32:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> - * client.c (connect_to_pserver): check return value of recv(). + * checkout.c (build_dirs_and_chdir): Partially revert 3 Jan + change--move call to Subdir_Register back above the CVS_CHDIR call + (we need to register in the old, not the new, directory). Instead + of calling CVS_MKDIR and ignoring errors, call mkdir_if_needed; + this is an effort to catch errors there rather than catching them + in the CVS_CHDIR. This makes test 27-add-add in sanity.sh work + again. + + * find_names.c (Find_Directories): Remove code inside + #ifdef ATTIC_DIR_SUPPORT and replace it with a comment explaining + why we don't look in the attic. ATTIC_DIR_SUPPORT was never defined. + + * find_names.c (find_dirs): Add comment about tmp being unset. + + * commit.c (checkaddfile): Report errors with errno and specific + error messages. + + * rcs.c, commit.c, create_adm.c, entries.c, find_names.c, + ignore.c, history.c: Nuke PATH_MAX arbitrary limits. + +Wed Jan 8 23:07:41 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c (add): Reindent a portion which needed it. + +1997-01-08 Jim Kingdon -Mon Jan 8 11:37:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * client.c (send_file_names): When looking name up in Entries, + call Entries_Open and Entries_Close. This has two effects: + (1) we look at Entries.Log, and (2) we don't skip 'D' entries, + both of which are needed to make us get the right (command + line) name for a directory we are adding. + +Wed Jan 8 14:50:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in, cvs.h, hash.h, myndbm.h, rcs.h: Remove CVSid; we + decided to get rid of these some time ago. - * client.c (connect_to_pserver): Check for error from connect; - reindent function. +Tue Jan 7 12:56:10 1997 Karl Fogel <kfogel@ynu38.ynu.edu.cn> - * sanity.sh (4.75): Use dotest, so we get a PASS if test passes. + * root.c (check_root_consistent): new func, compares below new + global var with CVSroot_directory, assuming both set. + (set_local_cvsroot): use above new func for security check. + (parse_cvsroot): same. + But do all of above only #ifdef AUTH_SERVER_SUPPORT. - * sanity.sh (dotest): New argument OUTPUT2. - (188a): Use it instead of \|. + * server.c: (Pserver_Repos): new global var, init to NULL. + (pserver_authenticate_connection): set above new global. + (check_repository_password): be a good scout and use + CVSROOTADM and CVSROOTADM_PASSWD, now that they are the standard. + Make sure all of above is in #ifdef AUTH_SERVER_SUPPORT. + (check_command_legal_p): wrap most of body in #ifdef + AUTH_SERVER_SUPPORT. + Everywhere: wrap all references to CVS_Username in #ifdef + AUTH_SERVER_SUPPORT. - * sanity.sh (import): Avoid using string $ followed by Id followed - by $ in sanity.sh source, in case sanity.sh itself is under CVS. - I hate keyword expansion. + * cvs.h (Pserver_Repos): new var, used in consistency [security] + check. Defined only #ifdef AUTH_SERVER_SUPPORT. + (CVSROOTADM_PASSWD): new #define, trying to get with the program. - * sanity.sh: If expr cannot handle multiline expressions, fail and - tell the user to get one which can. +Fri Jan 3 18:10:39 1997 Ian Lance Taylor <ian@cygnus.com> - * release.c (release_delete): Remove unused variable retcode. + * checkout.c (build_dirs_and_chdir): Move call to Subdir_Register + until after we know that the directory exists. + +Thu Jan 2 13:30:56 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Fri Jan 5 13:30:00 1996 Jim Kingdon <kingdon@peary.cyclic.com> + * Makefile.in, cvsbug.sh, edit.c, edit.h, error.c, error.h, + fileattr.c, fileattr.h, filesubr.c, run.c, update.h, watch.c, + watch.h: Remove "675" paragraph; see ../ChangeLog for rationale. - * release.c (release_delete): Call unlink_file_dir rather - than "rm -rf". +Thu Jan 2 12:27:46 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Thu Jan 4 09:58:30 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * sanity.sh (info): New test info-cleanup-verifymsg gets rid of + verifymsg when we are done with it. - * commit.c (find_fileproc): Print "nothing known about foo" and - return 1 if the file doesn't exist and isn't in CVS/Entries. - (commit): If the recursion over find_fileproc returns an error, - print "correct above errors first!" just like local CVS. - * sanity.sh (basica): Test for above-fixed bug. + * sanity.sh (basicb): Skip tests basicb-1a and basicb-9a for remote. + * sanity.sh (modules-155a4): It is OK if a CVS directory exists. + * sanity.sh (ignore): Do everything inside a "1" directory. The + change to create CVS directories at top-level causes messages such as + "? home" otherwise. In test 191, specify -I CVS so that new CVS + directory is ignored. + * sanity.sh (crerepos): Manually remove CVS directory which had not + existed before. - * release.c (release): If we are the client, only unedit if the - server supports it. +Thu Jan 2 09:06:20 1997 Karl Fogel <kfogel@ynu38.ynu.edu.cn> - * sanity.sh: Remove STARTANCHOR stuff; expr patterns are - automatically anchored to the start. ENDANCHOR remains. + * server.c: Changes for pserver read-only repository access: + (check_command_legal_p): new func. Right now, just checks if + repository modification is permitted, and that only if pserver is + active. + (do_cvs_command): take new parameter `cmd_name', a string. All + callers changed. Pass it on to new func check_command_legal_p() + before executing the command. + (CVS_Username): new global, init to NULL. Used by + check_command_legal_p(), set in check_password(). + (check_password): set above new global CVS_Username; reorganized a + bit to facilitate this. + (check_repository_password): give *host_user_ptr permanent + storage iff success. - * commit.c (commit): Don't start the server until we have - determined that there is something to commit. + * main.c: Changes for pserver read-only repository access: + (lookup_command_attribute): new func. + (main): use new func lookup_command_attribute() to establish if + CVS_CMD_IGNORE_ADMROOT and CVS_CMD_USES_WORK_DIR. -Thu Jan 4 09:48:33 1996 Ben Laurie <ben@gonzo.ben.algroup.co.uk> - and Jim Kingdon <kingdon@harvey.cyclic.com> + * cvs.h: Changes for pserver read-only repository access: + (CVSROOTADM_READERS, CVSROOTADM_WRITERS): new #defines. + Prototype lookup_command_attribute(). + (CVS_CMD_IGNORE_ADMROOT, CVS_CMD_USES_WORK_DIR, + CVS_CMD_MODIFIES_REPOSITORY): new #defines for + lookup_command_attribute() and its callers. - * client.c (start_server): dup the file descriptor before - fdopening it. +Wed Jan 1 19:50:38 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Jan 3 18:25:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + * options.h.in: Reword comment for TMPDIR_DFLT to make it clear + that this isn't specific to the pserver server. - * sanity.sh: Remove tests 5, 5.5, and 5.75. All that stuff is - tested elsewhere. + * modules.c (do_module): Give an error message if one tries to + specify -a together with another option. + * sanity.sh (modules2): New tests modules2-a* test for above fix. - * ignore.c (ign_default): Change CVS* to CVS CVS.adm. CVS* is too - broad, especially in a case-insensitive filesystem. + * sanity.sh (devcom): Add tests devcom-a-nonexist and + devcom-t-nonexist for "cvs watchers" on nonexistent argument. - * Makefile.in (cvsbug): version.c is in srcdir. +1997-01-01 Fred Fish <fnf@ninemoons.com> -Wed Jan 3 17:30:45 1996 Phi-Long Tran <ptran@autodesk.com> + * run.c (piped_child, filter_stream_through_program): Actually + install these HAVE_VFORK patches that got missed. + (There was a log entry for these changes for 29 Nov 1996 but it + seems I accidentally forgot to actually check them in -kingdon). - * modules.c (do_module): Honor error_use_protocol in printing trace. - * server.c (server_register): Move check for options NULL to above - printing of the trace. +Wed Jan 1 18:32:44 1997 Jim Kingdon <kingdon@harvey.cyclic.com> -Wed Jan 3 01:19:53 1996 Mark Immel <immel@centerline.com> - and Jim Kingdon <kingdon@harvey.cyclic.com> + * Makefile.in: Add ChangeLog-96. + * ChangeLog-96: New file, contains former contents of ChangeLog. + * ChangeLog: Now just contains 1997 changes. - * update.c (checkout_file): Do not resurrect file on join if it - doesn't contain the revisions we are joining. Probably not a - perfect test, but should be an improvement. - * sanity.sh (death): New death-file4-* tests, for bug fixed above. - -Wed Jan 3 01:19:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * add.c, admin.c, checkout.c, client.c, commit.c, diff.c, edit.c, - history.c, import.c, log.c, patch.c, release.c, remove.c, rtag.c, - status.c, tag.c, update.c, watch.c: In calling send_to_server, - pass \012 not \n. On the Mac \n is CR, not LF, and we want to - send LF. I didn't try to deal with whether files in CVSADM should - contain CR or LF--in fact there is some code in client.c which - reads \n from CVSADM files and passes it to send_to_server; it - needs to be cleaned up one way or the other. - - * entries.c (Entries_Open): Don't try to close fpin twice. - - * client.c (update_entries): Fix typo ("strlen (filename + 10)" - -> "strlen (filename) + 10"). - - * commit.c (checkaddfile): Remove arbitrary limit. - -Tue Jan 2 11:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * commit.c (commit): Only pass files which were modified, added, - or removed to send_file_names. This has as a side effect a - semantic change--the up-to-date check is now skipped for other - files--but probably a good one, or at least not a bad one. - * sanity.sh (basica): New test; tests for bug fixed above. - * sanity.sh (187a3): Adjust for new 'cvs commit' output. Set up - DOTSTAR to match arbitrary text (another GNU expr bug/misfeature, - sigh). - - * sanity.sh: Test that the commit in test 43 actually worked. - Merge tests basic2 and basic3 and make them independent of basic1. - (pass,fail): Don't insert spurious space. - (45.5): Fix typo in directory name. - -Tue Jan 2 13:00:00 1996 Jim Kingdon <kingdon@peary.cyclic.com> - - Visual C++ lint: - * myndbm.c: Prototype write_item. - -Tue Jan 2 11:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - gcc -Wall lint: - * client.c (client_expand_modules): Pass error message not "" to error. - * client.c (supported_request), server.c (supported_response): - Return a value (gcc -Wall can't know that error doesn't return). - * commit.c (copy_ulist): Return a value. - * history.c (fill_hrec): Don't make assumptions about whether - time_t is "int" or "long" or what. - * cvs.h: Declare link_file. - * server.c: Include fileattr.h. - * server.c (server_notify): Remove unused variable val. - * tag.c (val_fileproc): Remove unused variable foundtag. - -Mon Jan 1 09:49:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> - - * Version 1.6.5. - - * Version 1.6.4. - - * filesubr.c (link_file): Add comment about link vs. copy semantics. - - * cvs.h (struct vers_ts): Fix comments. - * commit.c (commit): Before we ask for a log message, figure out - what is modified and what is not and pass the information to - do_editor. - (copy_ulist,find_fileproc): New helper functions for above code. - - * client.c (read_line): When writing to from_server_logfile, write - the \n too. - - * client.c (send_files): No longer call send_file_names. - * client.h: Update comment. - * add.c, admin.c, commit.c, diff.c, edit.c, log.c, remove.c, - status.c, tag.c, update.c, watch.c: Call send_file_names before - send_files. - * client.c: New variables module_argc, module_argv. - (client_expand_modules): Set them, to arguments. - (client_send_expansions): Use them instead of modules_vector to - send arguments. - * sanity.sh (modules): Add test of modules -d flag. - - -For older changes see ChangeLog-9395. + +For older changes see ChangeLog-96. diff --git a/contrib/cvs/src/ChangeLog-96 b/contrib/cvs/src/ChangeLog-96 new file mode 100644 index 0000000..6c3a2a1 --- /dev/null +++ b/contrib/cvs/src/ChangeLog-96 @@ -0,0 +1,4434 @@ +Mon Dec 30 15:43:48 1996 Abe Feldman <feldman@harvey.cyclic.com> + + * checkout.c (build_dirs_and_chdir): Reproduced block containing + Create_Admin, placing it before Subdir_Register. + * sanity.sh (basicb): Added tests 1a and 9a to test above changes + to the checkout command. + +Mon Dec 30 13:29:14 1996 uz@wuschel.ibb.schwaben.com (Ullrich von Bassewitz) + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.h (CVSEDITPREFIXLEN): New define. + * logmsg.c (do_editor): Use CVSEDITPREFIXLEN when deciding whether + to strip off CVSEDITPREFIX and when telling the user what we will + strip off. + +Sun Dec 22 22:06:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * logmsg.c (do_verify): If noexec, skip the verification *without* + printing a message. Use cvs_output not printf. Skip verification + for client_active. + +Wed Dec 18 12:27:35 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * repos.c (Name_Repository): Add comment regarding wording of + "*PANIC*" error message. + +1996-12-18 Jim Kingdon + + * client.c (call_in_directory): If the directory we are about + to create is the same as CVSADM as seen by fncmp (for example, + it is "cvs" and filenames are case-insensitive), then give a + fatal error. + +Tue Dec 17 13:14:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * options.h.in: Add comments about SETXID security holes. + + * logmsg.c (do_verify): Reindent comments. Check errno if return + code from run_exec is -1, not if it is 1. + * sanity.sh (info): Move tests info-4 and info-8 to end and rename + them. Add verifymsg tests. Instead of forcibly removing loginfo, + remove it nicely (test info-11). + +Tue Dec 17 12:45:32 1996 Abe Feldman <feldman@cyclic.com> + + * commit.c, import.c: Call do_verify as well as do_editor. + * cvs.h (CVSROOTADM_VERIFYMSG): Define. + * logmsg.c, cvs.h (do_verify, verifymsg_proc): New functions. + (verifymsg_script): New variable. + * mkmodules.c (filelist): Add CVSROOTADM_VERIFYMSG. + +Mon Dec 16 13:24:47 1996 Ian Lance Taylor <ian@cygnus.com> + + * lock.c (remove_locks): New static function, copied from part of + Lock_Cleanup. + (Lock_Cleanup): Call remove_locks. + (Writer_Lock): Call remove_locks rather than Lock_Cleanup when + waiting for a lock. + +Thu Dec 12 10:36:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * login.c (get_cvs_password): If CVS_PASSWORD is set, print a + warning (and then proceed to ignore it). It was a documented + feature, so we should point people who were using it to the + replacement. + +Mon Dec 9 12:35:43 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (server_updated): Change comment to only worry about + umask in the rsh case. + (server): Create the temporary directory, and change the mode to + S_IRWXU. + (switch_to_user): Set the umask to 0, not 077. + +Mon Dec 9 10:58:28 1996 Jim Blandy <jimb@floss.cyclic.com> + + * login.c (get_cvs_password): Remove code to check for value of + CVS_PASSWORD. Keeping cleartext passwords in environment + variables is a really bad idea on Unix, since anyone can print + out a processes' environment using 'ps' (on BSD variants + anyway). Update help message. + +Fri Dec 6 15:59:40 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: When matching "use .cvs commit. to remove this file + permanently" messages, change "cvs" to "${PROG}". + (rdiff, binfiles): Likewise. + This fixes testing a program named something other than "cvs", e.g. + $ cp cvs cvs-test + $ /bin/sh <srcdir>/sanity.sh `pwd`/cvs-test + +1996-12-02 Jim Kingdon + + * client.c: In comment saying that socket buffers don't + implement the blocking routine, say they are blocking. + * buffer.h (struct buffer): In description of input function, + describe blocking, non-blocking, and NEED more fully. Say + what happens if we read a nonzero amount less than NEED and + then get end of file. + * client.c (socket_buffer_input): If NEED == 0, still call + recv (once). Handle the case where recv returns 0. + +Sat Nov 30 15:10:07 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * subr.c, cvs.h (file_has_markers): New function. + * rcs.h (RCS_MERGE_PAT): Now a fixed string not a regexp. + * options.h.in (GREP): Removed; no longer used. + * update.c (update_fileproc), commit.c (check_fileproc): Call + file_has_markers rather than GREP. + * rcscmds.c (RCS_merge): Just give a fatal error in the case where + we had been calling GREP. I suspect noone is using this code + any more. + * sanity.sh (conflicts): Rewrite tests 131, 132, and 133 to use + dotest; tests that the above changes didn't break anything. + +Fri Nov 29 09:06:41 1996 fnf@ninemoons.com (Fred Fish) + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * checkout.c (safe_location): Only call readlink if HAVE_READLINK. + + * run.c (piped_child, filter_stream_through_program): If + HAVE_VFORK, call vfork not fork. + * run.c (run_exec): Add comment about why we use vfork. + +Mon Nov 25 12:48:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * release.c (release): Don't return after processing the first + argument; that kind of defeats the purpose of having a loop, eh? + For client, close the connection after we've processed them all. + * sanity.sh: Remove workaround for modules2-8 test; tests for + above fix. Adjust modules2-6 test to answer both questions. + + * login.c: Reindent (all of get_cvs_password, a handful of lines + elsewhere). + + Cleanups to release, mostly cosmetic: + * release.c (release_server): New function; breaks out server code + from the release function. + * release.c: Move delete_flag inside the release function. + * release.c (release): Reindent. Rewrite comments about how the + implementation could be improved. Don't declare variables as + "register". Include errno in error message. Don't cast result of + printf to void. Remove unused variable srepos. + * release.c: Remove comments at top of file about what it does. + They were not particularly coherent and they were also out of date + (I think). Likewise for comment in release function about "if we + are in a repository". + * release.c: Change "module" to "directory" in a few messages + since that is what is meant. + * sanity.sh: In tests ignore-195 and ignore-193, change expected + message accordingly. + +Sun Nov 24 11:30:55 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Clarify a few items in the todo list. + + * log.c (log_parse_date): Use the "end" of the epoch not "next + week" as the time which means "no end time". + * sanity.sh (rcs): New test, tests dates and importing RCS files. + +1996-11-19 Jim Kingdon + + Visual C++ lint: + * hash.c: Declare qsort_comp. + * update.c: Declare isremoved. + +1996-11-19 Jim Kingdon <kingdon@harvey.cyclic.com> + + * root.c, repos.c, modules.c, create_adm.c: Change all calls to + strip_path to strip_trailing_slashes. Basically strip_path is + just an unneeded complication (we should keep the pathname the way + the user specifies it, and the system can worry about things like + consecutive /'s if it wants to). Stripping trailing slashes is + potentially dubious for the same reason, but it is a somewhat + different case which I won't try to tackle now. + * cvs.h (strip_path): Remove declaration. + +Tue Nov 19 15:18:13 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + There are a lot of details to this change, but the concept is + relatively simple: make it so that for every CVSLCK lock that we + might take out, there is a flag which is set iff we have created + the CVSLCK directory. + * lock.c (struct lock): New structure. + * lock.c: Remove static variables repository and cleanup_lckdir. + They are replaced by global_readlock.repository and + global_readlock.have_lckdir, respectively (except insofar as the + rest of these changes change the concept of cleanup_lckdir). + New static variable global_readlock. + (Reader_Lock, Lock_Cleanup): Use global_readlock in place of + repository. + (lock_simple_remove, set_lock, clear_lock, write_lock): Take a + struct lock * instead of just a repository. Set/clear + lock->have_lckdir instead of cleanup_lckdir. + (set_writelock_proc, unlock_proc): Pass ->data, not ->key, to + write_lock or lock_simple_remove. + (lock_filesdoneproc,lock_dir_for_write): Allocate a struct lock, + put it in the ->data field, and fill in its fields. + (lock_simple_remove): Use lock->have_lckdir as the sole test for + whether the CVSLCK directory needs to be removed. Add + comments about why readlock and writelock variables don't tell us + for sure whether locks exist. + (lock_simple_remove, clear_lock): Use SIG_beginCrSect and + SIG_endCrSect to ensure that ->have_lckdir is set to 0 iff the + CVSLCK directory was really removed. + (lock_simple_remove): Check for errors removing CVSLCK directory. + (lock_simple_remove, Check_Owner, set_lock): Remove all code which + checks userids (including all of Check_Owner and all the AFCVS + code). It was bogus if several CVS processes with the same userid + were running (common if several users share a userid; a common + practice with remote CVS), and with the rest of the changes here + should not be needed. + +1996-11-16 Paul Eggert <eggert@twinsun.com> + + * rcs.c (RCS_deltas): Fix unintended trigraphs. + +Fri Nov 15 13:06:03 1996 Tom Hageman <tom@basil.icce.rug.nl> + + * diff.c (diff_fileproc): In printing error messages, use the + correct filename for which the error occurred. + +Sun Nov 10 21:13:38 1996 Paul Sanders <p.sanders@dial.pipex.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c: Use all the right cruft which goes along with + including sys/time.h. + + * server.c: Include a "copyright" notice. + + * server.c: If HAVE_WINSOCK_H, include winsock.h. + + * server.c (server): Only set a handler for SIGHUP if it is + defined. Likewise for all the other signals. + + * server.c (do_cvs_command): Use DEVNULL not /dev/null. + +Fri Nov 08 12:14:20 1996 Jim Kingdon + + IBM ICC (OS/2) lint: + * add.c (add): Only declare begin_added_files if + SERVER_SUPPORT. + * client.c (init_sockaddr): Change port argument from + unsigned short to unsigned int. Change hostname + argument from const char * to char *. + +Sun Nov 3 18:24:28 1996 Noel Cragg <noel@gargle.rain.org> + + * sanity.sh (info): add new tests that check behavior of format + string substitution in loginfo file. + +Sat Nov 2 09:39:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (do_deferred_progs): Don't access memory once it is + freed (we already did it right for checkin_progs; do the same + thing for update_progs). + + * update.c, client.c, classify.c, client.h, diff.c, commit.c, + create_adm.c: Nuke more PATH_MAX. + +Fri Nov 1 18:22:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * error.h: Define PROTO if it is not defined. + +Wed Oct 30 08:53:20 1996 jalving@ibm.net + + * patch.c (patch_fileproc): Set line1 and line2 to NULL up-front + (before the first "goto out") so we don't try to free them. + +Wed Oct 30 08:53:20 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (remove_file, commit_filesdoneproc), run.c (run_print, + run_exec), modules.c (open_module, cat_module, do_module), update.c + (update_dirleave_proc), tag.c (tag_fileproc): Call cvs_out* rather + than stdio. + * server.c (serve_expand_modules): Remove comment about do_module + writing to stdout/stderr; above changes should fix this. + +Tue Oct 29 17:23:59 1996 Ian Lance Taylor <ian@cygnus.com> + + * status.c (tag_list_proc): When printing the tag name, don't + truncate it to 25 characters. + +Tue Oct 29 12:49:07 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c, checkin.c, checkout.c, filesubr.c: Nuke arbitrary limit + of PATH_MAX. Many more such limits surely remain. + + * fileattr.c (fileattr_set): Set attrs_modified *after* we might + call fileattr_read, because fileattr_read clears it. + * sanity.sh (devcom2): New tests, test for above fix and other + behaviors I discovered in the process of looking into it. + +Mon Oct 28 08:55:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + The following changes are to ensure that SYSTEM_CLEANUP is always + called. + * error.c, cvs.h, main.c: Remove error_set_cleanup and related + machinery. It was for a time when error.c was intended to be + shared with other programs, but that is no longer true. + * error.c, error.h (error_exit): New function; like error_cleanup + from main.c but also calls SYSTEM_CLEANUP and exit (EXIT_FAILURE). + * error.c (error, fperror): Call error_exit instead of doing it + ourself. + * server.c (server, serve_valid_responses, switch_to_user, + check_password, pserver_authenticate_connection, + kserver_authenticate_connection): Call SYSTEM_CLEANUP before exit. + * add.c, client.c, import.c, main.c, mkmodules.c, modules.c, + recurse.c, server.c, tag.c, update.c: Call error_exit () + instead of exit (EXIT_FAILURE). + +Sun Oct 27 08:34:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (conflicts): New test 128.5 tests "cvs co -p" in an empty + directory (like 126.5), but when the file has nonempty contents. + * rcs.c (RCS_checkout): If writing to stdout, use cvs_output + rather than fwrite. + * update.c (checkout_file): Call cvs_stderr not fprintf. + These changes should fix some out-of-order bugs which show up in + situations like conflicts-126.5 and conflicts-128.5. + + * mkmodules.c (checkout_file): Call RCS_checkout rather than + run_exec on RCS_CO. + +Sat Oct 26 18:29:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (rdiff): cd out of testimport directory and remove it + when done. + + * sanity.sh (info): Adjust tests to reflect fact that loginfo was + created by cvs init. + + * sanity.sh (ignore): Change test 187a1 to allow any number of + files in CVSROOT, not just modules. + + * sanity.sh (modules): In tests 148a0 and 148a1, don't expect a + module which defines CVSROOT to itself, since we don't define one + any more. Also change test to rewrite modules rather than append + to it (in case any previous tests are changed to do something with + modules). Change test 155b to allow any number of files in + CVSROOT, not just modules. + + * add.c (add_directory): Set rev_old and rev_new fields of struct + logfile_info to NULL (prevents us from trying to free them later). + * commit.c (find_fileproc), import.c (import): Likewise. + + * sanity.sh (crerepos): New tests, to test alternate ways of + creating a repository and related matters. + * sanity.sh: Remove tests 1 through 3 and related cruft; replace + them with a new test 1 which merely tests "cvs init". By doing + the obscure stuff in crerepos we avoid having to do all this stuff + any time we run any single test. + +Sat Oct 26 16:19:48 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * main.c (main): If HAVE_TZSET is #defined, call tzset. This is + harmless on all systems I know of, and required on some. + +Fri Oct 25 13:20:44 1996 Ian Lance Taylor <ian@cygnus.com> + + * diff.c (diff_file_nodiff): When setting use_rev1, only return + DIFF_SAME if empty_file is DIFF_DIFFERENT and ts_user is not + NULL. Don't get confused by a vn_user field of "0" or one + starting with '-'. + * sanity.sh (death2): Add new death2-diff-{1,2,7,8} tests for + above patch. Renumber existing death2-diff tests to make room. + +Fri Oct 25 12:38:29 1996 Jim Wilson <wilson@cygnus.com> + + * sanity.sh (death2): In tests death2-diff-{2,4,6,8,10}, allow "_" + in temp file names. The system (tmpnam or whatever) generates + these names so they vary from system to system. + +Fri Oct 25 07:52:44 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * logmsg.c (logfile_write): Give an error for several cases which + should not be legal. Adjust comments accordingly. + * mkmodules.c (loginfo_contents): Make description of loginfo much + more concise. This should be a reminder, not full documentation. + +Tue Oct 22 10:37:37 1996 Noel Cragg <noel@gargle.rain.org> + + * commit.c (update_delproc): free structure members rev_old and + rev_new if they have been allocated. + + * mkmodules.c: change loginfo_contents to include a description of + the new format string. + + * logmsg.c (logfile_write): change syntax of format string. + +Sat Oct 19 16:09:55 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + For reference, this takes CVS's text segment from 348348 bytes to + 347420 bytes. + * server.c (requests): Change Directory to rq_essential + per change in doc/cvsclient.texi. + * client.c: Remove use_directory and all code which executed if + it wasn't set. This includes the get_short_pathname function. + * server.c: Likewise, for use_dir_and_repos. + (serve_repository): Give a fatal error. + * server.c (requests): Remove Lost. Change Unchanged to rq_essential. + (serve_lost): Removed. + * server.c, server.h, client.c, vers_ts.c: Remove use_unchanged, + code to set it, and all code which executed if it wasn't set. + +Sat Oct 19 12:44:08 1996 J. Richard Sladkey <jrs@world.std.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * hash.c (sortlist, new function qsort_comp): Rewrite to use qsort + instead of insert sort. Changes algorithm from n^2 to n log n + (assuming qsort is implemented with quicksort or similar). + +Sat Oct 19 12:44:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (basic2): In test basic2-64, use -x and specify types + which exclude E; the test is not built to deal with E (or any + other new types). + +Sat Oct 19 12:00:00 1996 Mark Mitchell <mmitchell@usa.net> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (isemptydir, new function isremoved): Avoid pruning + directories that contain files marked for removal but not + comitted. + * update.c, update.h (isemptydir): Now extern, not static. + * update.c (isemptydir): New parameter might_not_exist handles + difference in functionality from old client_isemptydir. Bring + over the improved error checking from client_isemptydir. + * client.c (client_isemptydir): Removed; isemptydir now suffices. + * update.c (update_dirleave_proc), client.c + (process_prune_candidates): Update callers. + * sanity.sh (deep): Add tests deep-rm* for above fix. + +Fri Oct 18 15:53:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (devcom): Add tests devcom-some* to test watching just + a single file. + + * root.c (Name_Root): Use isabsolute to test whether a pathname is + absolute instead of checking for the first character being '/'. + (Reported by Antoine P. Brusseau <brusseau@jprc.com>). + + * commit.c (checkaddfile): Free rev only if it is non-NULL (thanks + to cwong@world.std.com (Christopher Wong) for diagnosing this; the + death2-15 test in sanity.sh hits it). + +Thu Oct 17 15:21:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Reenable rdiff tests. Delete rdiff-9 test to reflect + the fact that the change to add a -K option has not been + incorporated. Adjust rdiff-8 test to reflect the fact that the + change to change the default keyword expansion for the first + revision has not been incorporated. + * patch.c (patch_fileproc): Pass the symbolic revision to + RCS_checkout so that Name can be expanded correctly. Reinstates + one of the 30 Sep 96 changes and fixes a bug which the sanity.sh + rdiff test tests for. + + Reinstate change from 30 Sep 96: + * patch.c (patch): CLIENT_SUPPORT: send '-f' if NOT force_tag_match + + * client.c (process_prune_candidates): Do not ignore errors from + unlink_file_dir. + + * filesubr.c (deep_remove_dir): If rmdir returns an error other + than ENOTEMPTY or EEXIST, return -1 not 0. Add workaround for AIX + header bug. + +Tue Apr 30 08:21:27 1996 Mike Sutton <mike_sutton@dayton.saic.com> + + * checkout.c, history.c: added logging/reporting of cvs export + command + +Wed Oct 16 10:16:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Remove tests 4, 4.5, and 4.75; this functionality is + already tested by 45, 45.5 and other tests. + (ignore): New tests ignore-192, ignore-193, ignore-194, and + ignore-195 test output from "cvs release". + (modules2): New tests modules2-6, modules2-7, and modules2-8 test + ability of cvs release to handle multiple arguments. Since it + currently doesn't, the tests are kludged. + + * server.c, cvs.h (cvs_flushout): New function. + * recurse.c (do_file_proc): Call it. + * server.c (cvs_outerr): Call fflush (stdout) in non-server case. + * main.c (main): Don't call setvbuf. The code was incorrectly + checking for "patch" (it really is "rdiff"); the concern about + slowing down large amounts of output is not specific to rdiff + (it applies to "log" for example); and the above changes should + meet the need. + +Tue Oct 15 10:22:10 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + This is intended to facilitate some future cleanups to the + locking, but by itself it is a simple, conversative rearrangement: + * tag.c (locked_dir, locked_list): Move from here... + * lock.c: ...to here. + * lock.c (Lock_Cleanup): If locked_dir is set clean it up too. + * tag.c (tag_unlockdir): Removed; with the above change + Lock_Cleanup suffices. + * tag.c (tag_lockdir): Move from here... + * lock.c (lock_dir_for_write): ...to here. + * tag.c (tag_fileproc), rtag.c (rtag_fileproc): Update callers. + Move comments concerning why we are locking what we are from + tag_lockdir to here. + * tag.c (tag_filesdoneproc), rtag.c (rtag_filesdoneproc): + Update callers. + * lock.c (Writer_Lock): Made static. + * cvs.h: Update declarations. + * server.c (server_notify): Call lock_dir_for_write rather than + calling Writer_Lock ourselves. + + This is intended to facilitate some future cleanups to the + locking, but by itself it is a simple, conversative rearrangement: + * lock.c (Lock_Cleanup): Also dellist (lock_tree_list). + * lock.c, cvs.h (lock_tree_cleanup): Removed; with the above change + Lock_Cleanup suffices. + * commit.c, edit.c, watch.c: Change callers. + +Sat Oct 12 21:41:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (deep): Add comment about whether the deep-4b behavior + is considered desirable. + +Sat Oct 12 20:36:36 1996 Ian Lance Taylor <ian@cygnus.com> + + * client.c (client_send_expansions): Add build_dirs parameter. + Change all callers. + (send_dirent_proc): Get build_dirs from callerdat; if it is + zero, don't send a nonexistent directory to the server. + (send_files): Add build_dirs parameter. Change all callers. + * client.h (send_files): Update prototype. + (send_files_contents): Remove prototype for nonexistent function. + (client_send_expansions): Update prototype. + * sanity.sh (deep): Add deep-4b test for above patch. + +Fri Oct 11 14:07:12 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + gcc -Wall lint: + * logmsg.c (title_proc): Remove unused variables title and comma. + + * sanity.sh (modules2): Don't be picky about whether we are + checking in 1.3 or 1.2 of modules; it depends on whether we are + running all the tests or just some. + +Thu Oct 10 14:52:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c, server.h (server_dir): New variable. + (output_dir): If it is set, send it before the directory name. + * modules.c (do_module): Set it, in the case of & modules, and + restore it when done. + * sanity.sh (modules): Don't clean up first-dir before starting; + tests now clean up for themselves at the end. + (modules2): New tests, for above fix. + +Wed Oct 9 15:52:34 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * sanity.sh: Barf immediately if run as root. + + * rcs.c (RCS_getrevtime): When giving a date to get_date, use the full + year, not the year - 1900, so that dates after 1999 are parsed + correctly. (Change thanks to Paul Eggert.) + +Wed Oct 9 10:59:11 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + Clean up gcc -Wmissing-prototypes lint: + * cvs.h (admin, add, checkout, commit, diff, history, import, + cvslog, login, patch, release, cvsremove, rtag, status, tag): + Declare. + * server.c, main.c: Don't declare them here. Don't declare update + either (which is already declared in cvs.h). + * tag.c, cvs.h, main.c, server.c: Rename tag to cvstag to avoid + name conflicts. + * client.c (init_sockaddr, auth_server_port_number), entries.c + (Entnode_Create, Entnode_Destroy), hash.c (nodetypestring), + login.c (construct_cvspass_filename), server.c + (supported_response), wrapper.c (wrap_matching_entry): Make static; + prototype. + * hash.c (printlist): Prototype. + * myndbm.c (mydbm_load_file): Change declaration to prototype. + +Tue Oct 8 22:35:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (log2): Re-add these tests; they were deleted for 1.9 + (because they were thought to be destabilizing and/or due to + confusion/accident), but they can be put back now. + + * sanity.sh (death2): In tests death2-diff-{2,4,6,8,10}, allow "-" + or "%" in temp file names. The system (tmpnam or whatever) + generates these names so they vary from system to system. + +Tue Oct 8 12:37:09 1996 Ian Lance Taylor <ian@cygnus.com> + + * options.h.in (HAD_RCS4): Remove; no longer used. + +Sun Oct 6 15:58:11 1996 Noel Cragg <noel@gargle.rain.org> + + * The following changes address problem #56 in the GNATS database + on harvey.cyclic.com: + + * logmsg.c (str_list_format): new global -- contains the format + for items to be placed in str_list. + (Update_Logfile): move code that creates the "title" string... + (logfile_write): ...to here. Pull apart the filter program and + look for a format string, extracting it if there is one. + (title_proc): write a given filename/value based on the format + string. + + * commit.c (classify_file_internal): new routine, old code (needed + to use the code in more than one place). Determines the status + and version information about a file. + (check_fileproc): use classify_file_internal. Fill in the rev_old + field for the struct logfile_info. + (commit_fileproc): Fill in the rev_new field. + + * cvs.h (struct logfile_info): add two new fields -- rev_old and + rev_new -- that keep track of revision numbers across commits. + +Fri Sep 27 15:21:47 1996 Peter Wemm <peter@spinner.dialix.com> + + * logmsg.c (do_editor): Do not use editinfo if running on the client. + +Fri Oct 4 15:11:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (server_cleanup): Temporarily clear noexec when calling + unlink_file_dir. This is so we clean up the temp directory even + when the -n global option is specified. + +Wed Oct 2 10:47:33 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * client.c (send_repository): initialize some variables before + first usage + +Tue Oct 1 13:01:24 1996 Jim Blandy <jimb@floss.cyclic.com> + + Revert some of Greg's changes; they're welcome later, but we're + trying to keep CVS stable for pre-release testing at the moment. + * checkin.c, commit.c, cvs.h, diff.c, import.c, main.c, no_diff.c, + options.h.in, patch.c, rcs.c, rcs.h, rcscmds.c, sanity.sh, update.c: + Revert changes of Sep 29 and 30. + +Tue Oct 1 13:17:31 1996 Ian Lance Taylor <ian@cygnus.com> + + Make sure the server temporary directory is removed even if + Max-dotdot is used. + * server.c (orig_server_temp_dir): New static variable. + (serve_max_dotdot): Don't free server_temp_dir if it is the same + as orig_server_temp_dir. + (do_cvs_command): Use orig_server_temp_dir in error message. + (server_cleanup): Remove orig_server_temp_dir. + (server): Set orig_server_temp_dir. Remove incorrect indentation + of error message. + + * import.c (update_rcs_file): Restore new argument to + RCS_checkout, removed in last patch. + +Tue Oct 1 00:32:55 1996 Jim Blandy <jimb@floss.cyclic.com> + + * import.c: Revert Greg Woods' changes of Sep 30. We may want + them later, but not before 1.9. + +Mon Sep 30 23:31:01 1996 Jim Blandy <jimb@floss.cyclic.com> + + * log.c (log_fileproc): Now that we might actually find a "desc" + node in rcsfile->other, thanks to Ian's change below, we had + better deal correctly if we find a null pointer in it. + +Mon Sep 30 13:55:03 1996 Greg A. Woods <woods@most.weird.com> + + * main.c (main): don't set need_to_create_root for "cvs init" + either, just in case it's run from within a valid working + directory. + + * sanity.sh (testcvs): oops, forgot to comment out test version I + was using... + + * diff.c (diff_fileproc): use Diffbin instead of DIFF (3). + * patch.c (patch_fileproc): use Diffbin instead of DIFF. + * commit.c (check_fileproc): use Grepbin instead of GREP. + * rcscmds.c (RCS_merge): use Grepbin instead of GREP. + * update.c (patch_file): use Diffbin instead of DIFF. + (update_fileproc): use Grepbin instead of GREP. + * cvs.h (Diffbin): new declaration. + (Grepbin): new declaration. + (DIFFBIN_ENV): new manifest to name DIFFBIN environ var. + (GREPBIN_ENV): new manifest to name GREPBIN environ var. + * option.h.in (DIFFBIN_DFLT): renamed from DIFF. + (GREPBIN_DFLT): renamed from GREP. + * main.c (main): new variables diffbin_update_env and + grepbin_update_inv, ala rcsbin_update_env. + (main): new options -D diffbin and -g grepbin + (usg): describe new options -D diffbin and -g grepbin. + (Diffbin): new global variable for DIFF path. + (Grepfbin): new global variable for GREP path. + + * options.h.in (RCSBIN_DFLT): mention this needs to be set if + your PATH isn't set properly by rshd. + + * sanity.sh (rdiff): re-do Jim's change, but with the original + keywords I had intended (should be a bit more like real life), and + Jim's better RCS date and user matching form. + [I guess that's what I get for checking things in at 3am! ;-)] + +Mon Sep 30 17:00:20 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_reparsercsfile): Store desc field value in main RCS + node data, not in version specific data. + * sanity.sh: Enable log2 test (for local CVS only). + +Mon Sep 30 13:01:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (log2): New test, tests cvs add -m. Not yet enabled + in "tests" because CVS currently flunks this test. + + * sanity.sh (rdiff, basic2): Allow "cvs server" as well as "cvs + checkout" and friends in messages. In testing output of cvs + status, don't require a tab which isn't there for remote. Skip + test rdiff-9 for remote. In test basic2-64, add missing slash in + the pattern which gets used for remote. + + * sanity.sh (rdiff): Fix strings we were matching against which + got keyword-expanded in checking in sanity.sh. + +Mon Sep 30 03:21:37 1996 Greg A. Woods <woods@most.weird.com> + + * sanity.sh: change all regexpr literal '.' to '\.' + (basic2): why are tests 34 & 42 commented out (because + of 'diff -u'?)? + add tests 56[abc], 57a, and 58a to test import to the main + branch (i.e. branch '1'). + (rdiff): new test section for rdiff -K, etc. + (dotest): remove dotest.ex? before running a new test. + (dotest_fail): remove dotest.ex? before running a new test. + (dotest_internal): write expected output to dotest.exp, or if $4 + also used, to dotest.ex1 and dotest.ex2. + (patch): renamed this test to 'serverpatch'. + (dotest_lit): rename dotest.res to dotest.exp ala dotest(). + remove dotest.ex? before running a new test. + (DOTSTAR): mention the bug exists up to 1.12 + (ENDANCHOR): mention the bug exists up to 1.12 + (dotest_all_in_one): new function for debugging. + (dotest_line_by_line): new function for debugging. + (dotest_internal_debug): new function for debugging. + (dotest_internal): stop emulating the ancient tests and don't spew + the dotest.tmp contents onto $LOGFILE -- it's just too much + meaningless noise. Only do this if the test fails. Many tests + don't use dotest() yet, so this isn't quite so helpful as it might + otherwise be. + (TODO): mention CVS/* files, especially CVS/Root. + + * main.c (main): add a commented out piece of code to suggest that + there should be a function lookup_command_attribute() that could + tell us various things about internal commands, such as whether + they use CVS/Root, or if they're repository-only, or if they need + a working directory, etc.... + (main): don't set need_to_create_root if command doesn't use a + local working directory. + + * patch.c (patch): CLIENT_SUPPORT: send '-f' if NOT force_tag_match + + * error.c (fperror): protect declaration for un-defined __STDC__ + + * import.c (import): permit imports to a branch with zero dots, + i.e. the trunk. + (update_rcs_file): don't detect conflicts if importing to the + trunk. + (import): add hint that we should allow a module name, instead of + just a pathname relative to $CVSROOT. + (add_rcs_file): if importing to trunk, do it with ci(1). + + * import.c: XXX the following are all #if 0'ed out until a full + implementation can be designed.... + (cbranch): new variable to support conflict detection on another + branch set by -c. + (import): truncate -b and -c optarg if to fit in static storage. + (import_usage): describe -c + + * rcscmds.c (RCS_checkout): add new argument 'rcsver'. If rcsver + is set, turn on 'keywords' to force call to RCS_exec_checkout. + * rcs.c (RCS_exec_checkout): add new argument 'rcsver'. Pass + 'rcsver' to "co" with run_arg(). + * cvs.h: (RCS_checkout): add new argument 'rcsver' to prototype. + (RCS_exec_checkout): add new argument 'rcsver' to prototype. + * commit.c (remove_file): supply new argument to RCS_checkout. + * checkin.c (Checkin): supply new argument to RCS_checkout. + * diff.c (diff_fileproc): supply new argument to RCS_checkout. + (diff_file_nodiff): supply new argument to RCS_checkout. + * no_diff.c (No_Difference): supply new argument to RCS_checkout. + * update.c (checkout_file): supply new argument to RCS_checkout. + (patch_file): supply new argument to RCS_checkout. + (join_file): supply new argument to RCS_checkout. + + * patch.c: (o_options): new variable for -K + (rcsver): new variable for -V. + (patch): add -K flag which sets o_options, change -V to set + rcsver, send o_options and rcsver if in client mode. + (patch_fileproc): use RCS_checkout instead of RCS_fast_checkout in + order to ensure $Name is expanded, use o_options if set, or + options if set, or by default "-ko" when getting "old" file. + +Sun Sep 29 16:43:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcscmds.c: Replace comment at top of file concerning RCS library + with a reworded version based on discussion between me, Ian, Paul + Eggert, and JimB. + +Sun Sep 29 13:09:45 1996 Noel Cragg <noel@kiva.rain.org> + + * main.c (main): don't create/update CVS/Root when doing the "cvs + login" command. Consider: if the user executes "cvs login" with + the working directory inside an already checked out module, we'd + incorrectly change the CVS/Root file to reflect the CVSROOT of the + "cvs login" command. + + * login.c (login): if we're re-logging into a server for which a + .cvspass entry already exists, copy the temporary file to its home + location rather than renaming. Renaming doesn't work between + filesystems. After copying, unlink the temporary file. + +Fri Sep 27 05:24:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * diff.c: Add comment about --brief option. + + * README-rm-add: Removed; the information which was here is now in + cvs.texinfo. + * Makefile.in (DISTFILES): Remove README-rm-add. + +Wed Sep 25 10:00:00 1996 Larry Jones <larry.jones@sdrc.com> + + * Makefile.in (cvsbug): Add dependency on version.c. + +Wed Sep 25 09:01:48 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * filesubr.c (get_homedir), update.c (update): Reindent. + +Wed Sep 25 04:44:54 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * version.c (version_string): Bump to 1.8.86. + +Wed Sep 25 05:17:50 1996 Jim Blandy <jimb@floss.cyclic.com> + + * update.c (update): Don't neglect to pass the -kmumble options + to the server. + * sanity.sh (binfiles-sticky): New tests for above. + + * cvsrc.c (read_cvsrc): Deal correctly with lines that specify a + command, but no options; don't corrupt argv. + + * sanity.sh: When testing rsh, use the program specified by + the CVS_RSH environment variable, if it's set. Move test to top + of file, so it runs before all other tests (it's really a + meta-test). + + * filesubr.c (get_homedir): Use getpwuid to find the home + directory, if the HOME environment variable isn't set. + * ignore.c (ign_add_file): Call get_homedir to find the user's + home directory; this is more portable than calling getpwuid. + +Tue Sep 24 09:08:17 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * log.c (log_tree): When walking through branches, follow the + ->prev field rather than following ->next which insures that the + loop only executes once and we only see the last branch. + * sanity.sh (multibranch): Test "cvs log" too; tests for above fix. + +Mon Sep 23 09:55:22 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * options.h.in: Fixed some typos in the comments and reindented + them. + +Sat Sep 21 02:33:26 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * sanity.sh: If we're testing remote CVS, make sure rsh itself is + working before running any tests. It's confusing when basica-1 + fails just because you don't have the local host in your .rhosts + file. + + * version.c (version_string): Bump to 1.8.85. + +Thu Sep 19 09:15:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * options.h.in: Define SERVER_FLOWCONTROL, SERVER_HI_WATER, + SERVER_LO_WATER. Several large sites (FreeBSD, Cygnus) have been + pounding on this code without problems, and it doesn't seem to + have any significant downsides. + +Tue Sep 17 01:13:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * status.c (status_fileproc): Instead of a default case, set sstat + before the switch. This way gcc -Wall can detect a missed case. + Add explicit T_TITLE case. + +Tue Sep 17 00:09:44 1996 Assar Westerlund <assar@pdc.kth.se> + + * login.c (login): Print usage if argc < 0. + +Tue Sep 17 00:09:44 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lock.c: In comment, mention one more function of readlocks + (fileattr not updated atomically). Note similarity between + solutions #2 and #5. + + * checkout.c (safe_location): Do not reject a location merely + because it textually starts with hardpath; insist that it be + hardpath or a subdirectory thereof. + +Mon Sep 16 11:46:36 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (server_cleanup): Add comment about ignoring errors + from unlink_file_dir. + +Mon Sep 16 10:31:48 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c: Add support for -T <tmpdir> command line option. This + is needed for servers started via inetd. + (usg): Added line for -T. Improved -z documentation. + (main): Read default for tmpdir from the environment. Test for 'T' + in getopt loop. Use '/tmp' as ultimative fallback. Update + environment if possible. + + * cvs.h (TMPDIR_ENV): Added for -T <tmpdir> command line option. + + * options.h.in: Add TMPDIR_DFLT + + * import.c (update_rcs_file): Use global variable Tmpdir instead + of reading the environment. + + * server.c (server_cleanup): Use global variable Tmpdir instead of + reading the environment. Also, replace system("rm -rf") with + unlink_file_dir. + (server): Use global variable Tmpdir instead of reading the + environment. + +Thu Sep 12 1996 Jim Kingdon <kingdon@cyclic.com> + + * main.c (main): If ARGV0_NOT_PROGRAM_NAME, then just set + program_name to "cvs" rather than argv[0]. + +Thu Sep 12 12:06:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (update_entries): If we can't write the file, don't + make it a fatal error. + +Wed Sep 11 12:46:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_server): Move START_SERVER_RETURNS_SOCKET code + so that it is only run for server_method. It is wrong for + pserver_method (in which connect_to_pserver sets server_sock). + + * login.c (construct_cvspass_filename): If NO_SLASH_AFTER_HOME, + don't put a '/' between $HOME and .cvspass. Reindent function. + * build_src.com: Add zlib.c, login.c, and scramble.c. + + * rcs.c (RCS_deltas): When looking for our branch in ->branches, + check the branch number. + * sanity.sh (multibranch): New tests test for above fix. + + * commit.c (precommit_list_proc): Fix typo in last change + (->status to ->type). + +Tue Sep 10 23:05:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (DISTFILES): Add build_src.com. + * build_src.com: Add buffer.c, buffer.obj, and zlib.olb. + +Tue Sep 10 20:35:23 1996 Juergen Renz <renz@conware.de> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (precommit_list_proc): Update to reflect Jul 22 change + in which p->data was changed from a Ctype to a struct + logfile_info *. This means that commitinfo scripts again get + passed the file list like they should. + +Tue Sep 10 20:35:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (auth_server_port_number): Change name of service from + "cvs" to "cvspserver". The latter is what the manual has always + recommended, and it is also officially registered with IANA. + +Tue Sep 10 11:12:42 1996 Mark A. Solinski <markso@mcs.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (socket_buffer_output): Change ifdef VMS to ifdef + SEND_NEVER_PARTIAL. + (start_server): Change ifdef VMS to ifdef START_SERVER_RETURNS_SOCKET. + +Tue Sep 10 17:15:21 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * client.c (auth_server_port_number): Look up "cvs" in the + services database, and use the value it returns; fall back to + CVS_AUTH_PORT if no entry is present. + (connect_to_pserver): Use the correct port number in any error + messages. + +Tue Sep 10 11:12:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (newb): New test newb-123j0 tests for another "cvs + status" case. + +Sun Sep 8 15:20:37 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_checkout): Clarify handling of options parameter. + + * rcs.c (RCS_checkout): Free buffer allocated by RCS_deltas. + +Sat Sep 7 21:28:27 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (struct cmd): Add comment concerning recognizing unique + abbreviations. + +Fri Sep 6 22:31:52 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.c (RCS_checkout): Fix indentation. + +Fri Sep 6 11:48:08 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_checkout): Replace tag parameter with rev and nametag + parameters. Change all callers. + * rcs.h (RCS_checkout): Update declaration. + + * rcs.c (RCS_getversion): Replace return_both parameter with + simple_tag. Change all callers. + (RCS_gettag): Likewise. + * rcs.h (RCS_getversion, RCS_gettag): Update declarations. + * vers_ts.c (Version_TS): Simplify vn_tag initialization using new + simple_tag rather than old return_both. + * cvs.h (struct vers_ts): Clarify vn_tag comment a bit. + + * main.c (usg): Only mention -x if ENCRYPTION is defined. + (main): Mention ENCRYPTION define in comment for -x. + * client.h (krb_encrypt_buffer_initialize): Only declare if + ENCRYPTION is defined. + * client.c (start_server): Only encrypt if ENCRYPTION is defined. + * server.c (serve_kerberos_encrypt): Only define if ENCRYPTION is + defined. + (requests): Only include Kerberos-encrypt is ENCRYPTION is + defined. + (krb_encrypt_*): Only define if ENCRYPTION is defined. + +Thu Sep 5 17:32:39 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh: When testing remote, use :ext: instead of :server: to + match change made earlier today. + +Thu Sep 5 13:57:47 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_tcp_server): Don't allow :kserver: to mean + "direct tcp" (root.c already takes care of this, but I want to + make it clear what is intended, and not intended, here). + (start_server): Handle ext_method (external rsh program) and + server_method (internal rsh client) separately. + * client.c: Take rsh_pid and start_rsh_server out of + RSH_NOT_TRANSPARENT ifdefs. It is useful for things like SSH on NT. + * cvs.h (CVSmethod), root.c (method_names): Add ext_method. + * root.c (parse_cvsroot): Recognize "ext" access method. + If access method is not specified and CVSROOT contains a colon, + use either ext_method or server_method depending on + RSH_NOT_TRANSPARENT. + +Thu Sep 5 00:09:49 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_checkout): Remove flags parameter, which was not + serving any useful purpose. Change all callers. + * rcscmds.c (RCS_exec_checkout): Likewise. + + * rcscmds.c (RCS_exec_checkout): Rename from RCS_checkout. Change + all callers. + * rcs.c (RCS_checkout): Rename from RCS_fast_checkout. Change all + callers. + +Wed Sep 4 14:42:28 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_fast_checkout): If tracing, output a message. If + noexec, and workfile is not NULL, just return immediately. Assert + that sout is RUN_TTY or workfile is NULL, rather than using it as + a conditional. Replace found variable with two variables--gothead + and keywords--reflecting what it actually means. + + * rcs.c (RCS_fast_checkout): Don't handle the case of workfile set + to "". + * rcscmds.c (RCS_checkout): Likewise. + * checkin.c (Checkin): Pass explicit file name, not "", to + RCS_fast_checkout. + * update.c (join_file): Likewise. + * commit.c (remove_file): Pass explicit file name to + RCS_fast_checkout and RCS_checkin. + + * rcs.c (RCS_reparsercsfile): Always continue after seeing + RCSSYMBOLS, even if the value is NULL. Clear the NODELTA flag + after setting delta_pos. + (free_rcsnode_contents): New static function. + (freercsnode): Call free_rcsnode_contents. + (RCS_fast_checkout): If NODELTA is set, reparse the RCS file. + (RCS_settag): New function. Change all callers to old function. + (RCS_deltag, RCS_setbranch): Likewise. + (RCS_lock, RCS_unlock): Likewise. + (RCS_deltas): If NODELTA is set, reparse the RCS file. + * rcs.h (NODELTA): Define. + (RCS_settag, RCS_deltag, RCS_setbranch): Declare. + (RCS_lock, RCS_unlock): Declare. + * rcscmds.c (RCS_exec_settag): Rename from RCS_settag. Don't + check tag against BASE or HEAD (now done in new RCS_settag). + (RCS_exec_deltag): Rename from RCS_deltag. + (RCS_exec_setbranch): Rename from RCS_setbranch. + (RCS_exec_lock): Rename from RCS_lock. + (RCS_exec_unlock): Rename from RCS_unlock. + * cvs.h: Update declarations of renamed functions. + * checkin.c (Checkin): Remove rcscopy variable (no longer needed + because of change in RCS_unlock call). + * commit.c: Include <assert.h>. + (remove_file): Update RCSNode path if the file is renamed. + (unblockrcs): Change rcs parameter to RCSNode. Change all + callers. + (fixbranch): Likewise. + (lock_RCS): Likewise. Don't call RCS_parsercsfile. + (checkaddfile): Update RCSNode path if the file is renamed. After + creating a new file, call RCS_parse. When stubbing a branch, use + the passed in RCSNode if there is one, rather than calling + RCS_Parse. Don't call RCS_Parse again after calling RCS_settag. + Free head and magicrev even if RCS_settag fails. + * import.c (add_rev): Change rcs parameter to RCSNode. Change all + callers. + (add_tag): Likewise. + + * rcs.c (RCS_fast_checkout): Amend last patch: if workfile is + NULL, but sout is not NULL, use sout in error message. + +Wed Sep 4 13:35:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Increment version number to 1.8.8. + + * Version 1.8.7. + +Wed Sep 4 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (send_file_names): Look for the name to send in + Entries even if the file doesn't exist; we should send the + name as it appears in Entries in the "rm foo; cvs update FOO" + case. + +Tue Sep 3 20:50:11 1996 William A. Hoffman <hoffman@albirio.crd.ge.com> + + * rcs.c (RCS_fast_checkout): If workfile is NULL, don't try to + include it in error message. + +Mon Aug 26 12:27:38 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * mkmodules.c (mkdir_if_needed): Move from here ... + * filesubr.c, cvs.h (mkdir_if_needed): ... to here. Have it + return a value saying whether the directory was created. + * client.c (call_in_directory), edit.c (edit_fileproc): Call it. + +Fri Aug 23 19:19:44 1996 Ian Lance Taylor <ian@cygnus.com> + + * checkin.c (Checkin): Copy rcs parameter in case it is freed when + finfo->rcs is freed. + +Fri Aug 23 14:55:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * remove.c (remove_fileproc): Revert change of 23 Aug to print + getwd and finfo->file in message. The latter is redundant with + fullname and the former is redundant with fullname and the working + directory when CVS was invoked. The implementation was also + lacking as the getwd call could overflow the buffer. + +Fri Aug 23 18:40:35 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * remove.c (cvsremove): fix remove -f for client/server + +Fri Aug 23 11:28:27 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * wrapper.c, cvs.h: Remove conflictHook field of WrapperEntry, + WRAP_CONFLICT in WrapMergeHas, and 'c' option in wrap_add; they + are never used. + +Fri Aug 23 11:41:46 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * server.c (switch_to_user): use #ifdef SETXID_SUPPORT instead of + #if SETXID_SUPPORT + +Thu Aug 22 14:18:43 1996 Ian Lance Taylor <ian@cygnus.com> + + * checkin.c (Checkin): Remove local variable xfinfo. Reparse the + RCS file after the checkin. Call RCS_fast_checkout rather than + RCS_checkout. + + * cvs.h (RCS_FLAGS_LOCK): Don't define. + (RCS_FLAGS_*): Adjust values to fill in hole left by removal of + RCS_FLAGS_LOCK. + * rcs.c (RCS_fast_checkout): Don't check for RCS_FLAGS_LOCK. + * rcscmds.c (RCS_checkout): Likewise. + * commit.c (commit_fileproc): Remove rcs local variable. If + status is T_MODIFIED, require that finfo->rcs be set, call + Lock_RCS directly, and don't call locate_rcs. If adding to a tag, + require that finfo->rcs be set, and don't call locate_rcs. + (remove_file): Remove rcs local variable. Require that finfo->rcs + be set. Don't call locate_rcs. Don't pass RCS_FLAGS_LOCK to + RCS_checkout; use RCS_lock instead. Call RCS_fast_checkout rather + than RCS_checkout. + (unlockrcs): Use a single rcs parameter rather than two parameters + for file and repository. Change all callers. Don't call + locate_rcs. + (fixbranch): Likewise. + (lockrcsfile): Remove; no more callers. + +Tue Aug 20 10:13:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * buffer.c, rcs.c: Don't use inline. It wasn't being used in a + loop or any such place where it would matter for performance, and + it was a (minor) portability hassle. + + * server.c (server): Change "Dummy argument 0" to "cvs server" and + add comment explaining why. + + * rcs.c (linevector_add): Add comment regarding changing \n to \0. + +Tue Aug 20 09:19:19 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * checkout.c (checkout_proc): Call RCS_parse to get the default + options from the RCS file. + + * sanity.sh (binfiles): Add tests 5.5b0 and 5.5b1 for the above fix + +Mon Aug 19 18:13:32 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (linevector_init): Make inline. Set lines_alloced to 0, + not 10. Set vector to NULL. + (linevector_add): Remove assertion that lines_alloced is greater + than zero. Initialize lines_alloced if necessary. + (linevector_copy): Initialize lines_alloced if necessary. + (linevector_free): Only free vector if it is not NULL. + (RCS_deltas): Always call linevector_init and linevector_free on + curlines, headlines, and trunklines. + (RCS_fast_checkout): Remove #if 0 around code that calls + RCS_deltas. + +Fri Aug 16 17:52:54 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (linevector_add): Handle zero length correctly. + (RCS_deltas): In RCS_FETCH case, the data is in headlines, not + curlines. + (RCS_fast_checkout): Update comment about RCS_deltas: the + testsuite now passes. + + * rcs.c (RCS_fully_parse): Use the length of the value, rather + than assuming that there are no embedded zero bytes. + (struct line): Add len field. + (linevector_add): Add len parameter. Change all callers. Use + len, rather than assuming that there are no embedded zero bytes. + Set the len field in new lines. + (RCS_deltas): Use the length of the value, rather than assuming + that there are no embedded zero bytes. Use the line length when + outputting it and when copying it. + (RCS_fast_checkout): Update comment about RCS_deltas to remove + note about supporting zero bytes correctly. + +Thu Aug 15 23:38:48 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c, import.c: Revise comments regarding the fact that we + call start_server before do_editor. + +Thu Aug 15 11:30:55 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c: Include <sys/socket.h> if AUTH_SERVER_SUPPORT. + (pserver_authenticate_connection): Set SO_KEEPALIVE on + STDIN_FILENO. + (kserver_authenticate_connection): Likewise. + +Thu Aug 15 10:26:41 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * server.c (switch_to_user): Fix previous patch to compile it for + both HAVE_KERBEROS and AUTH_SERVER_SUPPORT + +Wed Aug 14 14:02:00 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * server.c (check_password): if available use getspnam instead of + getpwnam when reading system passwords. This allows cvs pserver + to run on systems with shadow passwords. + (switch_to_user): new static function. Contains the extracted + common tail of kserver_authenticate_connection and + pserver_authenticate_connection. If compiled with SETXID_SUPPORT, + honor the setgid bit if it is set. + (check_repository_password): turn into a static function + (check_password): ditto + (pserver_authenticate_connection): little code cleanup + +Wed Aug 14 01:07:10 1996 Greg A. Woods <woods@most.weird.com> + + * history.c (history): apply fix posted by Steven Meyer + <steve@blacksmith.com> to info-cvs to correct handling of '-D' + argument. Message-Id: <9608122335.AA01385@nijel.blacksmith.com> + +Tue Aug 13 13:42:36 1996 Ian Lance Taylor <ian@cygnus.com> + + * log.c (cvslog): Remove comment about calling rlog. + * rcs.c (translate_symtag): Correct typo in comment (l ist -> + list). + * server.c (server_write_entries): Add omitted word (lists) in + comment. + +Tue Aug 13 14:01:49 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * wrapper.c (wrap_rcsoption): fix memory access error + + * rcs.c (RCS_fast_checkout): fix memory access error (triggered + by an empty option string) + +Mon Aug 12 17:45:15 1996 Jim Kingdon (unknown@beezley) + + * buffer.c, zlib.c: If EIO is not defined, try to define it. + +Mon Aug 12 10:33:27 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * import.c (comtable): Add comment concerning applicability with + RCS 5.7. + + * server.c (server): If TMPDIR is not an absolute pathname, give + an error. + +Mon Aug 12 10:34:43 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c: add synonym "ann" for "annotate" again + +Sun Aug 11 17:54:11 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.h (RCS_RLOG): Removed; no longer used. + +Fri Aug 9 20:16:20 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (dirswitch): Open the Entries file with mode "a" rather + than "w+". + (server_write_entries): Open the Entries file with mode "a" rather + than "w". + * sanity.sh (modules): Add topfiles module and 155cN tests for + above patch. + +Fri Aug 9 12:11:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (cmd): Add comment regarding synonyms. + +Thu Aug 8 14:40:10 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c: Remove synonyms for "cvs annotate". Synonyms create + user confusion. + +Thu Aug 8 10:24:04 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c: Revert (undocumented) change to rename the cvs history + alias "his" to "hist" + +Wed Aug 7 18:26:25 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (cvs_output): Change str parameter to const char *. + Correct loop to print from p, not str. + (cvs_outerr): Likewise. + * cvs.h (cvs_output, cvs_outerr): Update declarations. + + * server.c (receive_partial_file): Read and discard remaining file + data on a write error. + (serve_modified): Discard data while size > 0, not >=. + +Wed Aug 7 15:11:40 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c (cmds): Add some aliases for "annotate". + (usg): Improve usage message text + (cmd_synonyms): New function to print the command synonym list + (main): Add new option --help-synonyms + +Wed Aug 7 00:07:31 1996 Ian Lance Taylor <ian@cygnus.com> + + Keep track of subdirectories in the Entries file. + * cvs.h (enum ent_type): Define. + (struct entnode): Add type field. + (struct stickydirtag): Add subdirs field. + (Subdirs_Known, Subdir_Register, Subdir_Deregister): Declare. + (ignore_files): Update declaration for new parameter. + (FILESDONEPROC): Add entries parameter. + (DIRENTPROC, DIRLEAVEPROC): Likewise. + * entries.c (Entnode_Create): Add type parameter. Change all + callers. + (write_ent_proc): If closure is not NULL, treat it as a pointer to + an int, and set it to 1 if a node is seen which is not ENT_FILE. + (write_entries): If subdirectory information is known, but no + subdirectories were written, write an unadorned D to the file. + (Scratch_Entry): Write an R command to Entries.Log. Don't rewrite + the Entries file. + (Register): Set entfilename. Write an A command rather than an + unadorned entries line. + (fgetentent): Add cmd and sawdir parameters. Change all callers. + If CMD is not NULL, expect and return a single character command. + Handle an initial D by setting the type to ENT_SUBDIR. + (fputentent): Output an initial D for an ENT_SUBDIR entry. + (Entries_Open): Handle removal commands in Entries.Log. Record + whether subdirectory information is known in the list private + data. + (Subdirs_Known): New function. + (subdir_record): New static function. + (Subdir_Register, Subdir_Deregister): New functions. + * find_names.c (add_entries_proc): Skip entries that are not + ENT_FILE. + (add_subdir_proc): New static function. + (register_subdir_proc): New static function. + (Find_Directories): If the Entries file has subdirectory + information, get the directories out of it. Otherwise, call + find_dirs, and add the information to the Entries file. + * recurse.c (struct frame_and_entries): Define. + (do_recursion): Don't call Entries_Close until after processing + dirlist. Pass entries to filesdoneproc. Pass a frame_and_entries + structure to do_dir_proc via walklist. + (do_dir_proc): Expect a frame_and_entries structure in closure, + not a recursion_frame. Pass entries to direntproc and + dirleaveproc. + * ignore.c (ignore_files): Add entries parameter. Change all + callers. If we have subdirectory information, check for + directories in entries. + * add.c (add): If client_active, call Subdir_Register on each new + directory. + (add_directory): Add entries parameter. Change caller. Call + Subdir_Register. + * checkout.c (build_dirs_and_chdir): Call Subdir_Register. + * client.c (call_in_directory): Call Subdir_Register for newly + created directories. Call Subdirs_Known or Find_Directories after + calling Entries_Open. + (process_prune_candidates): Call Subdir_Deregister. + * commit.c (findmaxrev): Skip entries that are not ENT_FILE. + * server.c (dirswitch): Call Subdir_Register. + * update.c (update_dirent_proc): Call Subdir_Register. + (update_dirleave_proc): Call Subdir_Deregister. + * Several files: Change direntproc, dirleaveproc, and + filesdoneproc routines to expect an entries argument. + + * rcs.c (translate_symtag): New static function. + (RCS_gettag): Use translate_symtag rather than RCS_symbols. + (RCS_nodeisbranch, RCS_whatbranch): Likewise. + +Tue Aug 6 15:36:09 1996 Ian Lance Taylor <ian@cygnus.com> + + Finish the conversion of cvs log so that it never invokes rlog. + * log.c (struct log_data): Remove dorlog field. Add nameonly, + header, long_header, statelist, and authorlist fields. + (log_usage): Remove rlog-options. Add -R, -h, -t, -b, -s, -w. + (cvslog): Don't clear opterr. Handle -h, -R, -s, -t, -w. If an + unrecognized option is seen, call usage. + (log_parse_list): New static function. + (log_fileproc): Remove code that called rlog. Check nameonly, + header, and long_header fields in log_data. + (log_version_requested): Check statelist and authorlist. + + * log.c (struct datelist): Define. + (struct log_data): Add datelist and singledatelist fields. + (log_usage): Add -d. + (cvslog): Handle -d. + (log_parse_date): New static function. + (log_fileproc): Do special single date handling. + (log_version_requested): Check datelist and singledatelist. + (log_fix_singledate): New static function. + +Mon Aug 5 23:48:16 1996 Ian Lance Taylor <ian@cygnus.com> + + * log.c (struct option_revlist): Define. + (struct revlist): Define. + (struct log_data): Add default_branch and revlist fields. + (struct log_data_and_rcs): Define. + (log_usage): Add -N and -r. + (cvslog): Handle -N and -r. + (log_parse_revlist): New static function. + (log_fileproc): Call log_expand_revlist and log_free_revlist. + Pass log_data_and_rcs structure to log_count_print via walklist. + (log_expand_revlist, log_free_revlist): New static functions. + (log_version_requested): New static function. + (log_count_print): New static function. + (log_tree): Add log_data and revlist parameter. Change all + callers. + (log_abranch): Likewise. + (log_version): Likewise. Call log_version_requested. + (version_compare): New static function. + * sanity.sh (log): New tests for -r, -b, and -N options to log. + +Sun Aug 4 11:19:30 1996 Ian Lance Taylor <ian@cygnus.com> + + Handle simple cases of cvs log without invoking rlog. + * log.c (struct log_data): Define. + (cvslog): Use getopt to parse options. Set up a log_data + structure, and pass it to start_recursion. + (log_fileproc): Get arguments form callerdat rather than static + variables. In simple cases, print the log information directly, + rather than invoking rlog. + (log_symbol, log_count, log_tree): New static functions. + (log_abranch, log_version, log_branch): New static functions. + * rcs.h (struct rcsnode): Add other field. + (struct rcsversnode): Add other field. + (RCS_fully_parse): Declare. + * rcs.c (getrcsrev): Move declaration to start of file. + (RCS_reparsercsfile): Add all parameter. Change all callers. + (RCS_fully_parse): New function. + (freercsnode): Free other list. + (rcsvers_delproc): Free other list. + * hash.h (enum ntype): Add RCSFIELD. + * hash.c (nodetypestring): Handle RCSFIELD. + +Sat Aug 3 19:39:54 1996 Ian Lance Taylor <ian@cygnus.com> + + * log.c (cvslog): Correct position of CLIENT_SUPPORT #endif. + +Thu Jul 25 12:06:45 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (join_file): If merging a branch, and the branch + revision does not exist, just return without doing anything. + * sanity.sh (join): Add cases file7 and file8 to test above + patch. + + * server.c (cvsencrypt): Rename from encrypt, to avoid conflict + with NetBSD unistd.h. Rename all uses. + + * server.c (krb_encrypt_buffer_output): Fix typo in comment (reply + -> replay). + +Thu Jul 25 10:37:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (krb_encrypt_buffer_output): Fix typo in comment + (krb_recv_auth -> krb_recvauth). + +Wed Jul 24 09:28:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lock.c (set_lock): Adjust comment regarding why we call stat. + +Wed Jul 24 15:06:08 1996 Ian Lance Taylor <ian@cygnus.com> + + Add encryption support over a Kerberos connection. + * main.c (usg): Mention -x if CLIENT_SUPPORT. + (main): Handle -x. + * client.h (encrypt): Declare. + (krb_encrypt_buffer_initialize): Declare. + * client.c (kblock, sched): New static variables if + HAVE_KERBEROS. + (start_tcp_server): Remove sched local variable. Copy + cred.session into kblock. + (start_server): Turn on encryption if requested. + * server.c (kblock, sched): New static variables if + HAVE_KERBEROS. + (serve_kerberos_encrypt): New static function. + (requests): Add "Kerberos-encrypt" if HAVE_KERBEROS. + (kserver_authenticate_connection): Remove sched local variable. + Copy auth.session into kblock. + (encrypt): New global variable. + (struct krb_encrypt_buffer): Define. + (krb_encrypt_buffer_initialize): New function. + (krb_encrypt_buffer_input): New static function. + (krb_encrypt_buffer_output): New static function. + (krb_encrypt_buffer_flush): New static function. + (krb_encrypt_buffer_block): New static function. + (krb_encrypt_buffer_shutdown): New static function. + +Wed Jul 24 09:28:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * recurse.c (do_recursion): Add comment about calling + Name_Repository in !(which & W_LOCAL) case. + + * expand_path.c (expand_variable): Fix typo (varaible -> variable). + +Tue Jul 23 15:05:01 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (update_fileproc): In T_REMOVE_ENTRY case, only call + server_scratch_entry_only if ts_user is NULL. + * sanity.sh (death2): Add death2-20 test for above patch. + + * diff.c (diff_fileproc): If a file is not in the working + directory, check that the tag is present before warning that no + comparison is possible. + * sanity.sh (death2): Add death2-diff-9 and death2-diff-10 tests + for above patch. + +Tue Jul 23 12:05:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * tag.c (tag_check_valid): Fix indentation. + + * client.c (handle_e): Flush stdout before writing to stderr. + (handle_m): Flush stderr before writing to stdout. + +Fri Jul 19 16:02:11 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * client.c: Added NO_CLIENT_GZIP_PROCESS to deal with the MacOS + client where Gzip-stream is supported, but "gzip-file-contents" is + not. + +Fri Jul 19 16:02:11 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * repos.c: Fixed recent patch which added plain fopen rather than + CVS_FOPEN + +Mon Jul 22 22:25:53 1996 Ian Lance Taylor <ian@cygnus.com> + + * logmsg.c (tag): New static variable. + (setup_tmpfile): Don't print the prefix before calling fmt_proc. + Free tag if it is set. + (find_type): Get type from logfile_info struct. + (fmt_proc): Likewise. Print tag information. Handle all prefix + printing. + (revision): Remove static variable. + (Update_Logfile): Remove xrevision parameter. Change all + callers. + (title_proc): Get type from logfile_info struct. + (logfile_write): Remove revision parameter. Change all callers. + * cvs.h (struct logfile_info): Define. + (Update_Logfile): Update prototype. + * commit.c (find_fileproc): Set logfile_info information. + (check_fileproc): Likewise. + (commit_filesdoneproc): Don't call ParseTag. + (update_delproc): Free logfile_info information. + * add.c (add_directory): Set logfile_info information. + * import.c (import): Likewise. + + * tag.c (tag_check_valid): The special BASE and HEAD tags are + always valid. + * sanity.sh (basica): Add basica-6.3 test for above patch. + +Mon Jul 22 14:41:20 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (merge_file): Pass 0 not NULL to checkout_file (20 Jul + 96 change changed other calls to checkout_file but missed this one). + +Sat Jul 20 00:21:54 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (join_file): Check whether the target of the merge is + the same as the working file revision before checking whether the + file was added during the merge. + + * update.c (scratch_file): Remove existing parameters, and add a + single parameter of type struct file_info. Change all callers. + Warn if unlink_file fails. + (checkout_file): Remove resurrecting_out parameter. Add adding + parameter. Change all callers. Remove joining code. + (join_file): Remove resurrecting parameter. Rewrite to handle + joining dead or added revisions. + * classify.c (Classify_File): If there is no user file, and the + RCS file is dead, return T_UPTODATE rather than T_CHECKOUT. + * checkout.c (checkout_proc): Set W_ATTIC if there is a join tag. + * sanity.sh (join): New set of tests for above patches. + (death): Adjust tests 86, 89, 89a, 92.1c, 95 for above patches. + (import): Adjust test 113 for above patches. + +Thu Jul 18 19:24:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lock.c: Add comment explaining what locks are for. Also discuss + various changes to locking which get proposed from time to time. + + * sanity.sh (death2): Change a number of test names from death-* + to death2-*. + + * wrapper.c (wrap_setup): Don't look in repository if client_active. + * wrapper.c, cvs.h (wrap_send): New function. + * update.c (update), import.c (import): Call it. + * sanity.sh (binwrap): Do binwrap tests for remote as well as + local; tests for above fixes. + + * wrapper.c: Add a few FIXME comments. + +Thu Jul 18 18:43:50 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh (patch): Fix names of a couple of tests to say patch + rather than death2. + +Thu Jul 18 16:19:21 1996 Bill Bumgarner <bbum@friday.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c (add), import.c (add_rcs_file): Check for options from + wrappers and use them if specified. + * cvs.h (WrapMergeHas): Add WRAP_RCSOPTION. + * wrapper.c (WrapperEntry): Add rcsOption field. + (wrap_add): Allow a single character argument to an option. + (wrap_add): Handle -k option. + (wrap_add_entry): Handle rcsOption field. + (wrap_name_has): Handle WRAP_RCSOPTION. + * wrapper.c, cvs.h (wrap_rcsoption): New function. + * add.c, import.c, wrapper.c: Minor beautification (mostly + removing trailing spaces). + * sanity.sh (binwrap): New tests test for this feature. + +Wed Jul 17 10:14:20 1996 Ian Lance Taylor <ian@cygnus.com> + + * checkout.c (checkout): Remove extraneous else accidentally + inserted in last checkin. + +Tue Jul 16 11:37:41 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh (import): Use quoting to avoid expansion of RCS ID + strings. + + * sanity.sh (import): Use dotest to examine the output of test + 113, and the actual contents of the file in test 116. + + * update.c (join_file): Always skip rcsmerge if the two revisions + are the same (the old code always did the rcsmerge when two -j + options were specified). + + * checkout.c (history_name): New static variable. + (checkout): Permit both tag and date to be specified. Set + history_name. + (checkout_proc): Use history_name when calling history_write. + * rcs.c (RCS_getversion): If both tag and date are set, use + RCS_whatbranch to get the branch revision number of a symbolic + tag. + (RCS_getdatebranch): If the branch revision itself is early + enough, then use it if the first branch is not early enough. Add + comment for invalid RCS file. Don't bother to check for NULL + before calling xstrdup, since xstrdup checks anyhow. + + * client.h (file_gzip_level): Declare. + * client.c (file_gzip_level): Define. + (start_server): Don't set gzip_level to zero after sending + Gzip-stream command. Set file_gzip_level after sending + gzip-file-contents command. + (send_modified): Use file_gzip_level rather than gzip_level. + * server.c (server_updated): Likewise. + (serve_gzip_contents): Likewise. + + * sanity.sh (patch): New tests. Test remote CVS handling of + unpatchable files. + + * sanity.sh (death2): Accept a '.' in the temporary file name + printed by diff. + + * rcscmds.c (RCS_checkin): Remove noerr parameter. Change all + callers. + * cvs.h (RCS_checkin): Update declaration. + * commit.c (remove_file): Pass RCS_FLAGS_QUIET to RCS_checkin. + + * history.c (history): Cast sizeof to int to use correct type in + error printf string. + (report_hrecs): Cast strlen result to int to use correct type in + printf string. + + * server.c (cvs_flusherr): Correct typo in comment. + + * rcs.c (getrcskey): Hoist three constant strcmp calls out of the + value reading loop. + + * fileattr.c (fileattr_get): Change parameter types from char * to + const char *. + (fileattr_get0, fileattr_modify, fileattr_set): Likewise. + (fileattr_newfile): Likewise. + * fileattr.h (fileattr_get): Update declaration. + (fileattr_get0, fileattr_modify, fileattr_set): Likewise. + (fileattr_newfile): Likewise. + +Thu May 16 11:12:18 1996 Mark P. Immel <immel@radix.net> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.h, client.c, checkout.c (client_send_expansions): + Pass an additional parameter indicating where the checkout is + to occur, to avoid passing the wrong information to send_files(). + * sanity.sh (basicb): New test basicb-cod-1 tests for above fix. + +Mon Jul 15 18:26:56 1996 Ian Lance Taylor <ian@cygnus.com> + + * recurse.c (do_recursion): Require a repository before calling + Find_Names. + * repos.c (Name_Repository): Remove sanity checks which spend time + examining the filesystem. + +Mon Jul 15 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (send_file_names): Send file names as they appear + in CVS/Entries, rather than as specified (in cases where they + might differ in case). + (send_fileproc): Use file name from CVS/Entries (vers->entdata->user) + rather than file name as specified (finfo->file) when available. + +Sun Jul 14 15:39:44 1996 Mark Eichin <eichin@cygnus.com> + and Ian Lance Taylor <ian@cygnus.com> + + Improve diff -N handling of nonexistent tags and removed files. + * diff.c (enum diff_file): New definition for whole file, moving + unnamed enum out of diff_fileproc, renaming DIFF_NEITHER to + DIFF_DIFFERENT, and adding DIFF_SAME. + (diff): Look through the repository even if only one revision is + given. + (diff_fileproc): Change empty_file to be enum diff_file. If there + is no user revision, but there is a repository file, treat it as a + removed file. Pass empty_file to diff_file_nodiff, and set it + from the return value. + (diff_file_nodiff): Change return type to enum diff_file. Replace + just_set_rev parameter with enum diff_file empty_file parameter. + Change handling of a missing tag to return an enum diff_file value + if empty_files is set, rather than reporting an error. Free tmp + if xcmp returns 0. + * sanity.sh (death2): Add tests for above patches. + +Sat Jul 13 19:11:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.c (annotate): In sending options to server, reverse sense of + test so that we send -f iff -f was specified, rather than iff -f was + not specified. + +Fri Jul 12 20:23:54 1996 Greg A. Woods <woods@most.weird.com> + + * zlib.c (compress_buffer_input): add a couple of casts for + uses of z_stream's next_in and next_out + +Fri Jul 12 18:55:26 1996 Ian Lance Taylor <ian@cygnus.com> + + * zlib.c: New file. + * client.c (log_buffer_block): Call set_block and set_nonblock, + rather than lb->buf->block. + (log_buffer_shutdown): New static function. + (get_responses_and_close): Call buf_shutdown on to_server and + from_server. + (start_server): If "Gzip-stream" is supported, use it rather than + "gzip-file-contents". + * server.c (print_error): Call buf_flush rather than + buf_send_output. + (print_pending_error, serve_valid_responses): Likewise. + (serve_expand_modules, serve_valid_requests): Likewise. + (do_cvs_command): Call buf_flush rather than buf_send_output + before the fork, and in the parent after the child has completed. + In the child, set buf_to_net and buf_from_net to NULL. + (serve_gzip_stream): New static function. + (requests): Add "Gzip-stream". + (server_cleanup): Don't do anything with buf_to_net if it is + NULL. Call buf_flush rather than buf_send_output. Call + buf_shutdown on buf_to_net and buf_from_net. Call error for an + malloc failure rather than buf_output to buf_to_net. + * buffer.h (struct buffer): Add shutdown field. + (buf_initialize): Update declaration for new shutdown parameter. + (compress_buffer_initialize): Declare. + (buf_shutdown): Declare. + * buffer.c (buf_initialize): Add shutdown parameter. Change all + callers. + (buf_shutdown): New function. + * Makefile.in (SOURCES): Add zlib.c + (OBJECTS): Add zlib.o. + ($(PROGS)): Depend upon ../zlib/libz.a. + (cvs): Link against ../zlib/libz.a. + (zlib.o): New target. + +Fri Jul 12 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (log_buffer_input, log_buffer_output): Use size_t + to avoid Visual C++ signed/unsigned warnings. + +Thu Jul 11 22:01:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (handle_f): Reindent. + + * client.c (mode_to_string, handle_m, handle_e, + auth_server_port_number, get_responses_and_close), server.c + (pserver_authenticate_connection, serve_modified, + serve_enable_unchanged, wait_sig, server_cleanup): Reindent. + * server.c: Remove #if 0'd block of code above + check_repository_password; it was yanked out of some unknown + context and didn't seem to be very useful. + +Thu Jul 11 20:10:21 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (do_cvs_command): Pass new special parameter to + buf_copy_counted. If it gets set to -1, send an 'F' response if + the client supports it, and call cvs_flusherr. + (cvs_flusherr): New function. + * cvs.h (cvs_flusherr): Declare. + * client.c (handle_f): New static function. + (responses): Add "F". + * buffer.c (buf_send_special_count): New function. + (buf_copy_counted): Add special parameter. Handle negative counts + specially. + * buffer.h (buf_send_sepcial_count): Declare. + (buf_copy_counted): Update declaration. + * lock.c (lock_wait, lock_obtained): Call cvs_flusherr. + + Change the client to use the buffer data structure. + * client.c: Include "buffer.h". + (to_server): Change to be struct buffer *. + (to_server_fp): New static variable. + (from_server): Change to be struct buffer *. + (from_server_fp): New static variable. + (from_server_logfile, to_server_logfile): Remove. + (buf_memory_error): New static function. + (struct log_buffer): Define. + (log_buffer_initialize, log_buffer_input): New static functions. + (log_buffer_output, log_buffer_flush): New static functions. + (log_buffer_block): New static function. + (struct socket_buffer): Define if NO_SOCKET_TO_FD. + (socket_buffer_initialize): New static function if + NO_SOCKET_TO_FD. + (socket_buffer_input, socket_buffer_output): Likewise. + (socket_buffer_flush): Likewise. + (read_line): Rewrite to use buf_read_line. Remove eof_ok + parameter (it was always passed as 0); change all callers. + (send_to_server): Rewrite to use buf_output. + (try_read_from_server): Rewrite to use buf_read_data. + (get_responses_and_close): Use from_server_fp and to_server_fp for + the streams. Check buf_empty_p when checking for dying gasps. + (start_server): Don't set from_server_logfile and + to_server_logfile; instead, call log_buffer_initialize. If + NO_SOCKET_TO_FD and use_socket_style, call + socket_buffer_initialize; otherwise, call + stdio_buffer_initialize. + * buffer.c: Compile if CLIENT_SUPPORT is defined. + (buf_flush): Fix comment to describe return value. + (buf_read_line): Add lenp parameter. Change all callers. Look + for a line terminated by \012 rather than \n. + * buffer.h: Compile if CLIENT_SUPPORT is defined. + (buf_read_line): Update declaration. + + * server.c (server): Initialize buf_to_net, buf_from_net, + saved_output, and saved_outerr before setting error_use_protocol. + (pserver_authenticate_connection): Don't set error_use_protocol. + Errors before the authentication is complete aren't handled + cleanly anyhow. Change error call after authentication to use + printf. + +Thu Jul 11 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (start_server): Open logfiles in binary, not text, mode. + +Wed Jul 10 19:24:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (print_pending_error, print_error): Remove comments + about deadlocks; they don't apply here. Add comments saying + that these functions must only be called when it is OK to + send output (which is why the deadlock concern doesn't apply). The + comments remain for server_cleanup and serve_valid_responses, + where they are an example of the "print a message and exit" + behavior which is noted in cvsclient.texi and which also exists + places like kserver_authenticate_connection. + +Wed Jul 10 18:24:46 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (print_error): Add comment warning about potential + deadlock. + (print_pending_error, serve_valid_responses): Likewise. + (server_cleanup): Likewise. + (serve_directory): Don't call buf_send_output. + (serve_modified, serve_notify, server, cvs_outerr): Likewise. + (serve_expand_modules): Call buf_send_output. + (serve_valid_requests): Likewise. + +Wed Jul 10 15:51:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): Print a warning for rlog command. + +Wed Jul 10 15:00:55 1996 Ian Lance Taylor <ian@cygnus.com> + + Abstract the buffer data structure away from the underlying + communication medium. + * buffer.h (struct buffer): Remove fd and output fields. Add + input, output, flush, block, and closure fields. + (buf_initialize, buf_nonio_initialize): Declare. + (stdio_buffer_initialize, buf_flush): Declare. + (buf_read_line, buf_read_data): Declare. + * buffer.c: Include <assert.h>. Don't include <fcntl.h>. + (O_NONBLOCK, blocking_error): Don't define. + (buf_initialize, buf_nonio_initialize): New functions. + (buf_send_output): Use output function, rather than write. + (buf_flush): New function. + (set_nonblock, set_block): Use block function, rather than calling + fcntl. + (buf_send_counted): Don't check output. + (buf_input_data): Call input function, rather than read. + (buf_read_line, buf_read_data): New functions. + (buf_copy_lines, buf_copy_counted): Don't check output. + (stdio_buffer_initialize): New function. + (stdio_buffer_input, stdio_buffer_output): New static functions. + (stdio_bufer_flush): New static function. + * server.c: Include "getline.h". + (buf_to_net): Change to be a pointer. Change all uses. + (protocol, saved_output, saved_outerr): Likewise. + (buf_from_net): New static variable. + (no_mem_error, NO_MEM_ERROR, read_line): Remove. + (struct fd_buffer): Define. + (fd_buffer_initialize, fd_buffer_input): New static functions. + (fd_buffer_output, fd_buffer_flush): New static functions. + (fd_buffer_block): New static function. + (serve_directory): Call buf_read_line rather than read_line. + (serve_notify, server): Likewise. + (receive_partial_file): Call buf_read_data rather than fread. + (serve_modified): Call buf_read_line rather than read_line. Call + buf_read_data rather than fread. + (do_cvs_command): Initialize buffers with fd_buffer_initialize. + Change stdoutbuf, stderrbuf, and protocol_inbuf to be pointers. + (server): Initialize buffers using fd_buffer_initialize, + stdio_buffer_initialize, and buf_nonio_initialize. + (check_repository_password): Call getline rather than read_line. + +Wed Jul 10 15:51:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (find_fileproc): Add comments describing a few cases + that we aren't handling. + +Tue Jul 9 04:33:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.c (RCS_deltas): New function, created from guts of old + annotate_fileproc. + (annotate_fileproc): Call RCS_deltas. + (RCS_fast_checkout): Call it (commented out for now; see comment + for reasons). + + * cvs.h, recurse.c (start_recursion): Add callerdat argument. + * cvs.h: Add callerdat argument to recursion processor callbacks. + * recurse.c: add it to struct recursion_frame and pass it to all + the callbacks. + * admin.c, client.c, commit.c, diff.c, edit.c, lock.c, log.c, + patch.c, rcs.c, remove.c, rtag.c, status.c, tag.c, update.c, + watch.c: Update all the functions used as callbacks. Update calls + to start_recursion. + * commit.c (find_filesdoneproc, find_fileproc, find_dirent_proc, + commit), tag.c (val_fileproc, tag_check_valid): Use callerdat + instead of a static variable. + + * recurse.c (do_recursion): Make static and move declaration to here... + * cvs.h: ...from here. + * recurse.c (do_recursion): Replace plethora of arguments with + single struct recursion_frame *. Change callers. + * recurse.c: New structure frame_and_file. Use it and existing + struct recursion_frame structures to pass info to do_file_proc and + do_dir_proc. Remove globals fileproc, filesdoneproc, direntproc, + dirleaveproc, which, flags, aflag, readlock, and dosrcs. + +Tue Jul 9 11:13:29 1996 Ian Lance Taylor <ian@cygnus.com> + + * modules.c (do_module): Call cvs_outerr rather than fprintf. + +Mon Jul 8 1996 Jim Kingdon <kingdon@cyclic.com> + + * rcs.c (RCS_fast_checkout): If -kb is not in use, open the + working file in text, not binary, mode. + +Sun Jul 7 10:36:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcscmds.c (RCS_settag): Add comment regarding moving check for + reserved tag names to RCS_check_tag. + + * rcscmds.c: Add comment regarding librarifying RCS and related + issues. This is a lightly edited version of a message I sent to + the CVS developers and didn't get flamed for, so it would appear + to be relatively uncontroversial. + + * rcs.c (annotate): Remove comment suggesting -r option and + related functionality; it is done. + +Fri Jul 5 17:19:57 1996 Ian Lance Taylor <ian@cygnus.com> + + * client.c (last_entries): Make file static, rather than function + static within call_in_directory. + (get_responses_and_close): If last_entries is not NULL, pass it to + Entries_Close. + + * server.c (server_pause_check): Check for errors when reading + from flowcontrol_pipe. + + * client.c (call_in_directory): If dir_name is ".", call + Create_Admin if there is no CVS directory. + (send_dirent_proc): If there is no CVS subdirectory, pretend that + the directory does not exist (i.e., don't try to send any files in + the directory). + * server.c (dirswitch): If dir is "." in the top level repository, + add "/." after the Repository entry. + * sanity.sh (modules): Add test 155b for above patches. + +Thu Jul 4 15:57:34 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (buf_to_net): Move definition near top of file. + (read_line): Call buf_send_output rather than fflush. + (print_error): Output information to buf_to_net buffer rather than + stdout. + (print_pending_error, serve_valid_responses): Likewise. + (server_notify, do_cvs_command, server_co): Likewise. + (expand_proc, serve_expand_modules, server_prog): Likewise. + (serve_valid_requests, server_cleanup, server): Likewise. + (server_notify): Don't call fflush on stdout. + (do_cvs_command): Flush saved_output and saved_outerr to + buf_to_net before fork. Flush buf_to_net before fork. In child, + just initialize memory_error field of saved_output and + saved_outerr. + (server_cleanup): Flush buf_to_net. + (server): Initialize saved_output and saved_outerr. + (cvs_output): Add support for error_use_protocol case. + (cvs_outerr): Likewise. + * error.c (error): In HAVE_VPRINTF case, just call cvs_outerr. + + * buffer.c: New file; buffer support functions taken from + server.c. + * buffer.h: New file; declarations for buffer.c. + * server.c: Move buffer support functions into buffer.c and + buffer.h. Include "buffer.h". + * Makefile.in (SOURCES): Add buffer.c. + (OBJECTS): Add buffer.o. + (HEADERS): Add buffer.h. + +Thu Jul 4 00:12:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Increment version number to 1.8.6. + +Wed Jul 3 22:31:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Version 1.8.5. + +Wed Jul 3 21:51:23 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (blocking_error): Define macro. + (buf_send_output, buf_input_data): Use blocking_error rather than + #ifdef EWOULDBLOCK. + +Tue Jul 2 20:38:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c (add): Change message which said "version 1.2 of foo.c + will be resurrected"; the message was confusing because it made + people think that the old contents of the file would come back + instead of the contents in the working directory. + +Mon Jul 1 01:38:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * find_names.c (find_dirs): Add comment explaining why we bother + with the entries stuff. + +Sat Jun 29 20:23:50 1996 Ian Lance Taylor <ian@cygnus.com> + + * find_names.c (Find_Directories): Add entries parameter, and pass + it to find_dirs. + (find_dirs): Add entries parameter, and skip all files it names. + * cvs.h (Find_Directories): Update declaration. + * recurse.c (start_recursion): Pass NULL to Find_Directories. + (do_recursion): Pass entries to Find_Directories. + + * client.c (send_modified): Add trace output. + + * diff.c (diff_fileproc): Always call diff_file_nodiff. Handle + dead versions correctly. Handle diffs between a specified + revision to a dead file correctly. + (diff_file_nodiff): Add just_set_rev parameter. Change caller. + * patch.c (patch_fileproc): Check for dead versions. + * sanity.sh (death2): Add tests for above patches. + +Fri Jun 28 20:30:48 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + For reference, this takes CVS's text segment from 271136 bytes to + 270352 bytes, a saving of 784. Not as good as I had hoped (oh well, + the source *seems* simpler at least). + * checkin.c (Checkin), commit.c (finaladd, remove_file), update.c + (join_file, checkout_file, patch_file), no_diff.c + (No_Differences), server.c (server_updated), classify.c + (Classify_File), vers_ts.c (Version_TS), diff.c (diff_file_nodiff): + Use a single struct file_info * argument instead of a bunch of + separate arguments for each of its fields. Remove local fullname + emulations. Use fullname in error messages where file had + erroneously been used. + * cvs.h: Update declarations of above functions and move them to + after the struct file_info declaration. + * server.h: Update declarations. + * add.c, admin.c, checkin.c, checkout.c, classify.c, client.c, + commit.c, diff.c, history.c, import.c, update.c, status.c, + remove.c, rtag.c, tag.c: Change callers. + + * diff.c (diff): Remove -q and -Q command options. This somehow + slipped through the cracks of the general removal of -q and -Q + command options on Jul 21 1995. Note that there is no need to + accept and ignore these options in server mode, like there is for + some of the commands, because the client has never sent -q and -Q + command options for "cvs diff". + +Fri Jun 28 16:50:18 1996 Ian Lance Taylor <ian@cygnus.com> + + * add.c (add): Pass force_tag_match as 1 when calling Version_TS. + * sanity.sh (death2): Add test for above patch. Also add + commented out test for adding a file on a nonbranch tag, which CVS + currently, mistakenly, permits. + +Thu Jun 27 23:20:49 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * diff.c (longopts): New static array. + (diff): Handle long options and new short options in diff 2.7. + Fix arbitrary limit associated with the tmp variable. + * client.c (send_option_string): Parse options as space separated, + rather than requiring all options to be single characters. + * diff.c, options.h.in: Remove CVS_DIFFDATE; the need for it is gone + now that we have --ifdef (the new behavior is the behavior which + was the default, which is that -D specifies a date). + +Wed Jun 26 22:36:29 1996 Ian Lance Taylor <ian@cygnus.com> + + * commit.c (check_fileproc): If there is a tag, permit adding a + file even if the RCS file already exists. + (checkaddfile): If there is a tag, use the file in the regular + repository, rather than the Attic, if it exists. + * sanity.sh (death2): New set of tests for above patch. + +Tue Jun 25 23:34:13 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (checkout_file): Add comments about two cases which + seem fishy. + + * sanity.sh (basic2, death): Add comments encouraging people to + stop making these sections bigger and more complex. I'm not (yet + at least) trying to figure out the ideal size for a section (my + current best estimate is 10-20 tests), but surely these + two sections are pushing the limit, whatever it is. + +Tue Jun 25 19:52:02 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (checkout_file): Rewrite handling of dead files when + joining. Avoid space leaks. Avoid unnecessary file + resurrections. + (join_file): Add checks to skip merging a dead revision onto a + dead revision, and to skip merging a common ancestor onto a dead + revision. Move check for non-existent working file after new + checks. + * sanity.sh (death): Use dotest for tests 86 and 95, and add test + death-file2-1, to test above changes. + +Mon Jun 24 11:27:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (merge_file): Replace file, repository, entries, and + update_dir arguments with finfo argument. Use fullname field + instead of locally emulating it. + (update_fileproc): Update caller. + (merge_file): If -kb is in effect, call it a conflict, leave + the two versions in the file and the backup file, and tell the + user to deal with it. The previous behavior was that the merge + would fail and then there was no way to do a checkin even once you + resolved the conflict (short of kludges like moving the file + aside, updating, and then moving it back). + * sanity.sh (binfiles): New tests binfiles-con* test for above + behavior. Adjust remaining tests to reflect changes in revision + numbers. + +Mon Jun 17 15:11:09 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh (import): Remove sleep. Requiring it was a bug, and + it is fixed in the current sources. + +Mon Jun 17 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (TMPPWD): Set to real name of /tmp directory. + (basic2-64, conflicts-126.5): Use ${TMPPWD}. + +Mon Jun 17 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcscmds.c (RCS_checkout): Remove noerr parameter. Change all + callers. + * rcs.c (RCS_fast_checkout): Likewise. + +Mon Jun 17 1996 Ian Lance Taylor <ian@cygnus.com> + + Cleaner implementation of tag locking code added Jun 13 1996: + * cvs.h (tag_lockdir, tag_unlockdir): Declare. + * rtag.c (locked_dir, locked_list): Remove. + (rtag_fileproc): Don't lock here; just call tag_lockdir. + (rtag_filesdoneproc): Don't unlock here; just call tag_unlockdir. + * tag.c (locked_dir, locked_list): Move farther down in file. + (tag_fileproc): Don't lock here; just call tag_lockdir. + (tag_filesdoneproc): Don't unlock here; just call tag_unlockdir. + (tag_lockdir, tag_unlockdir): New functions. + +Wed Jun 15 07:52:22 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * client.c (send_modified, update_entries): Fixed bug which didn't + handle binary file transfers in BROKEN_READWRITE_CONVERSION. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (checkout_file): Call server_scratch_entry_only when a + non-pertinent file is found that does not exist. + * sanity.sh (newb): Add test case for above patch. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (update_fileproc): Call server_scratch_entry_only when + handling T_REMOVE_ENTRY on the server. + * sanity.sh (conflicts2): Remove special case for remote server + bug fixed by above patch. + +Thu Jun 13 21:16:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (basica-9): Update to reflect change to "sufficient + access" message. + +Thu Jun 13 20:13:55 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * recurse.c, cvs.h (start_recursion): Remove wd_is_repos argument; + add comment about meaning of which argument. Use !(which & + W_LOCAL) instead of wd_is_repos. + * admin.c, client.c, commit.c, diff.c, edit.c, lock.c, log.c, + patch.c, rcs.c, remove.c, rtag.c, status.c, tag.c, update.c, + watch.c: Change callers. This is a semantic change in only two + cases: (1) tag_check_valid, where repository was not "", and (2) + the pipeout case in checkout_proc. In both of those cases the + previous setting of wd_is_repos did not reflect whether we + actually were cd'd into the repository. + * recurse.c (start_recursion): Only check for the CVS subdirectory + if which & W_LOCAL. + * sanity.sh (devcom): Add test case fixed by above patch. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + * ignore.c (ignore_files): Skip based on the file name before + calling lstat. + + * client.c (last_register_time): New static variable. + (update_entries): Set last_register_time when calling Register. + (get_responses_and_close): If the current time is the same as + last_register_time, sleep for a section to avoid timestamp races. + +Thu Jun 13 17:24:38 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (supported_request): Reindent. + +Thu Jun 13 1996 Mark H. Wilkinson <mhw@minster.york.ac.uk> + + * options.h.in, mkmodules.c: Corrections to allow compilation of + non-client-server version. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + * tag.c (tag_check_valid_join): New function. + * cvs.h (tag_check_valid_join): Declare. + * checkout.c (join_tags_validated): New static variable. + (checkout_proc): Check validity of join tags. + * update.c (update): Likewise. + + * tag.c (tag_check_valid): Correct sizeof CVSROOTADM_HISTORY to + use CVSROOTADM_VALTAGS. + + * lock.c (Writer_Lock): If we called lock_wait to wait for a lock, + then call lock_obtained when we get it. + (set_lock): Likewise. + (lock_obtained): New static function. + +Thu Jun 13 13:55:38 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): If we can't read cvs root, don't say "you don't + have sufficient access"; just print the message from errno. It + might be "No such file or directory" or something else for which + "you don't have sufficient access" doesn't make any sense. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + * commit.c (remove_file): Pass noerr as 0 to RCS_checkout. + +Thu Jun 13 12:55:56 1996 Ian Lance Taylor <ian@cygnus.com> + + * patch.c: Initialize rev1_validated and rev2_validated to 0, not 1. + +Thu Jun 13 12:55:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rtag.c (locked_dir): Revise comments regarding locking; the rtag + and tag situations are different (changing from readlocking one + directory at a time to writelocking one directory at a time does + not do everything we might want, but it does fix simultaneous tags + and it doesn't make anything worse). + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + Prevent simultaneous tag operations from interfering with each + other. + * rtag.c (rtag_proc): Pass rtag_filesdoneproc to start_recursion, + and pass readlock as 0. + (locked_dir, locked_list): New static variables. + (rtag_fileproc): Write lock the repository if it is not already + locked. + (rtag_filesdoneproc): New static function to unlock the + repository. + * tag.c (tag): Pass tag_filesdoneproc to start_recursion, and pass + readlock as 0. + (locked_dir, locked_list): New static variables. + (tag_fileproc): Write lock the repository if it is not already + locked. + (tag_filesdoneproc): New static function. + +Thu Jun 13 11:42:25 1996 Mike Sutton <mws115@llcoolj.dayton.saic.com> + + * sanity.sh: Allow digits in usernames. + +Wed Jun 12 16:23:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (send_modified, update_entries): Reindent and add + comments to BROKEN_READWRITE_CONVERSION code. + +Wed Jun 12 16:23:03 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * client.c (send_modified, update_entries): Add + BROKEN_READWRITE_CONVERSION code. + +Mon Jun 10 20:03:16 1996 J.T. Conklin <jtc@cygnus.com> + + * rcs.c (RCS_gettag): No longer set p to NULL if rcs is also NULL. + rcs will never be null, thanks to the assertion at top of function. + +Mon Jun 10 16:28:14 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): Ignore CVS/Root file when doing an import. + +Fri Jun 7 18:20:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * status.c (status_fileproc, tag_list_proc): Use cvs_output rather + than writing to stdout directly. + +Wed Jun 5 13:54:57 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (force_tag_match, tag, date): New static variables. + (annotate_fileproc): Redo the loop to look for the version + specified by tag/date/force_tag_match, and handle branches + correctly. + (annotate_usage): Mention -f, -r, and -D. + (annotate): Handle -f, -r, and -D. + +Tue Jun 4 13:38:17 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (annotate_fileproc): Skip unrelated branch deltas. + +Fri Jun 7 13:04:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): Change INITIALIZE_SOCKET_SUBSYSTEM to + SYSTEM_INITIALIZE and pass it pointers to argc and argv. Rename + CLEANUP_SOCKET_SUBSYSTEM to SYSTEM_CLEANUP. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * import.c (add_rcs_file): make buf char[] not unsigned char[] + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * main.c (main): Add CLEANUP_SOCKET_SUBSYSTEM hook at end. Revise + comments regarding INITIALIZE_SOCKET_SUBSYSTEM. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * main.c (main): Don't mess with signals if DONT_USE_SIGNALS is + defined. + +Thu Jun 6 15:32:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * modules.c (cat_module): Always format for 80 columns rather than + trying to determine how wide the screen is. The code we had for + the latter didn't cover all cases, was a portability headache, and + didn't work client/server. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * error.c: Don't declare strerror if it is #defined. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * cvs.h: If ENUMS_CAN_BE_TROUBLE, typedef Dtype to int not an enum. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * update.c (update): If DONT_USE_PATCH, don't request patches. + Also call supported_request rather than reimplementing it. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * client.c (read_line): Changed an occurence of '\n' to '\012'. + +Wed Jun 5 17:18:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c (add_directory): Don't create the directory if noexec. + * sanity.sh (basica): New tests basica-1a10, basica-1a11 test for + above fix. + * sanity.sh (basicb): New tests basicb-2a10, basicb-2a11, + basicb-3a1 test for analogous situation with files rather than + directories. + +Tue Jun 4 13:38:17 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh: When doing a remote check, use :server: in CVSROOT. + +Wed Jun 5 13:32:40 1996 Larry Jones <larry.jones@sdrc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * ignore.c: Set ign_hold to -1 when not holding instead of 0 so + that holding an empty list works correctly. + * sanity.sh (ignore): New tests 190 & 191 for above fix. + +Wed Jun 5 1996 Jim Kingdon <kingdon@cyclic.com> + + Visual C++ lint: + * client.c (update_entries): Copy the size to an unsigned variable + before comparing it with unsigned variables. + (handle_created, handle_update_existing): Prototype. + +Tue Jun 4 10:02:44 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (responses): Add Created and Update-existing responses. + * server.c (server_updated): If they are supported, use them + instead of Updated. + * client.c (struct update_entries_data): Add existp field. + (handle_checked_in, handle_updated, handle_new_entry, + handle_merged, handle_patched): Set it. + (handle_update_existing, handle_created): New functions, + for new responses. + (update_entries): Based on existp, check for + existence/nonexistence of file. + (try_read_from_server): Expand comment. + * server.c, server.h (server_updated): New argument vers. + * checkin.c (Checkin), commit.c (commit_fileproc), update.c + (update_fileproc, merge_file, join_file): Pass it. + * cvs.h: Move include of server.h after Vers_TS declaration. + * sanity.sh (conflicts2): New tests conflicts2-142d* test for + above fix. + + * sanity.sh (ignore): Fix typo in comment. + + * tag.c (tag_check_valid): Add comment clarifying when val-tags + entries are created. + +Mon Jun 3 07:26:35 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Increment version number to 1.8.4. + +Mon Jun 3 02:20:30 1996 Noel Cragg <noel@gargle.rain.org> + + * version.c: version 1.8.3. + +Thu May 30 10:07:24 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (cmds): Fix typo ("bdif" -> "dif") which was accidentally + introduced 24 May 96. + + * main.c (main_cleanup): Add comment stating default case will + never be reached. + +Wed May 29 21:43:43 1996 noel <noel@BOAT_ANCHOR> + + * main.c (main_cleanup): check to see if SIGHUP, SIGINT, SIGQUIT, + SIGPIPE, and SIGTERM are defined before using them. Also add a + default case to print out those errors numerically which are not + found. + +Wed May 29 18:43:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * expand_path.c (expand_path): Document LINE == 0 and allocation + of return value. + * modules.c (do_module): Pass 0, not -1, to indicate line number + not known. Free value returned from expand_path. Deal with NULL + return from expand_path. + +Wed May 29 15:56:47 1996 Greg A. Woods <woods@most.weird.com> + + * modules.c (do_module): call expand_path() on the program name + specfied by one of '-o', '-t', or '-e' in the modules file before + passing it to run_setup(). This makes it possible to use $CVSROOT + (or indeed ~user or any other user-specified variable) to specify + pathnames for programs not installed in the normal execution path. + +Sun May 26 21:57:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_server): Don't include %s in error message; + there is no argument to go with it. Do include "internal error" + in error message since that might not be clear to the user otherwise. + +Sun May 26 11:58:13 1996 Greg A. Woods <woods@most.weird.com> + + * root.c (set_local_cvsroot): enforce a wee bit of portability + (parse_cvsroot): same.... + (DEBUG main): same, plus style guidelines + (DEBUG error): deleted -- not necessary here (use fprintf instead) + + * mkmodules.c (modules_contents): updated notes about what must be + done if you change any of the options for a module. + (loginfo_contents): fixed grammar, re-pargraphed, and added 'echo + %s;' to the example. + (editinfo_contents): minor grammar fix. + +Sun May 26 17:51:18 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * vers_ts.c (Version_TS): Remove case where we get options from + sdtp->options. Whatever case that was intended to handle is + probably lost in the mists of time, but sdtp->options isn't set + anywhere, and I think that has been true for a long time. + * cvs.h (struct stickydirtag): remove options field. + * entries.c (freesdt): Don't free ->options. + * sanity.sh (binfiles): New tests binfiles-13a* test for above fix. + + * tag.c (check_fileproc): Use fullname not file in error message. + Say "locally modified" not "up-to-date"; the file need not match + the head revision it only need match some revision. + +Sun May 26 16:57:02 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * tag.c: added support for new option -c to make sure all tagged + files are up-to-date + (tag): check for option and set check_uptodate + (check_fileproc): check status of file if check_uptodate is set + +Sat May 25 15:22:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): Revert change to look for a -H command option; + command option parsing should be up to each subcommand and the -H + global option works fine. + +Mon May 23 1996 Ian Lance Taylor <ian@cygnus.com> + + * client.c (process_prune_candidates): Set prune_candidates to + NULL at the end of the function. + +Mon May 23 1996 Ian Lance Taylor <ian@cygnus.com> + + * checkout.c (checkout): In code to handle multiple arguments, + pass preload_update_dir, not where, to Create_Admin. + (checkout_proc): Pass preload_update_dir, not where, to + Create_Admin. + +Thu May 23 19:14:35 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (server_set_sticky): Assert that update_dir != NULL. + * sanity.sh (basicb): New test; tests for Ian's fix to checkout.c + above. + +Thu May 23 1996 Ian Lance Taylor <ian@cygnus.com> + + * patch.c (patch_fileproc): Don't ignore a file just because it is + in the Attic directory. + +Thu May 23 10:40:24 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (death): New tests death-{72a,76a0,76a1} test for bug + fixed by Ian's patch_fileproc change above. + + * sanity.sh (death): Remove "temporary hack" in test 89. + + * rcs.c (RCS_fast_checkout): If error closing file, and workfile + is NULL, use sout in error message instead of workfile. + +Thu May 23 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_fast_checkout): Do a fast checkout in the case where + workfile is NULL and sout is a file name. + +Wed May 22 19:06:23 1996 Mark Immel <immel@centerline.com> + + * update.c (checkout_file): New arg resurrecting_out, to provide + resurrecting flag to caller. + (join_file): New arg resurrecting. Register with "0" if we are + the server and are resurrecting. + (update_fileproc): Pass the flag from checkout_file to join_file. + +Wed May 22 19:06:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (death): Test for above fix, in test 89 and new test 89a. + +Tue May 21 09:49:04 1996 Greg A. Woods <woods@most.weird.com> + + * update.c (update_usage): oops -- fix my spelling typo. + +Mon May 20 10:53:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (find_fileproc): Call freevers_ts. + + * commit.c (find_*): Keep an ignlist, like update.c and client.c do. + * commit.c (commit): Process the files from the ignlists, once we + are connected to the server. + * sanity.sh (ignore): New tests 189e and 189f test for new + commit.c behavior (and client.c behavior, which is unchanged). + * sanity.sh (conflicts): Remove dir1 and sdir in parts of the test + where we aren't prepared for "? dir1" and similar output. + +Mon May 20 13:23:36 1996 Greg A. Woods <woods@most.weird.com> + + * main.c (cmd_usage): minor corrections to descriptions of status, + rtag, tag, and rdiff. Sort alphabetically by command name. + +Mon May 20 10:36:07 1996 Ian Lance Taylor <ian@cygnus.com> + + * client.c (call_in_directory): Move the call to Entries_Close + before the call to chdir, since Entries_Close examines files in + the current directory. + +Fri May 17 12:13:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_tcp_server, start_server, start_rsh_server, + read_line, filter_through_gzip, filter_through_gunzip, + call_in_directory): Reindent as needed. + + * main.c (main): Add missing #endif. Use indentation to indicate + nesting. + +Thu May 16 17:15:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (cmd_usage): Add "init" command. + +Thu May 16 16:45:51 1996 Noel Cragg <noel@gargle.rain.org> + + * client.c (start_tcp_server): Error message modified to tell the + user to use ":server:" instead of setting CVS_CLIENT_PORT to a + negative number. + + * main.c (main): Add #ifdefs for turning off buffering of + stdio/stderr, so we don't get it by default. + +Thu May 16 01:29:47 1996 noel <noel@BOAT_ANCHOR> + + * commit.c (commit_filesdoneproc): Print the repository and root + directories as part of the error message. + + * main.c (main): Don't buffer stdout or stderr. It's inefficient, + but it then produces the right output for sanity.sh. + +Thu May 16 09:44:47 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * fileattr.c (fileattr_set): In the case where we are about to + call delproc, don't free ->data; delproc does that. + * sanity.sh (devcom): New tests devcom-b* test for this fix. + + * sanity.sh (conflicts): Remove redundant clean up from previous + tests at the beginning of the test. Use dotest a few more places. + (conflicts2): New test, tests for Ian's fix to Classify_File. + + * client.c (remove_entry_and_file): Add comment about + existence_error's. + +Sat May 16 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (update_dirleave_proc): Don't try to chdir .. and check + for an empty directory if there is a slash in the directory name. + +Thu May 16 09:02:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (deep): New tests deep-4a* test for Ian's fix to + update_dirleave_proc. + +Sat May 16 1996 Ian Lance Taylor <ian@cygnus.com> + + * main.c (main_cleanup): Report signal name before dying. + +Wed May 15 23:47:59 1996 Noel Cragg <noel@gargle.rain.org> + + * main.c (usg): revert usage strings for `-H' flag change. + +Sat May 15 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (serve_static_directory): Return immediately if there + is a pending error. + (serve_sticky): Likewise. + (serve_modified): Read the file data even if there is a pending + error. + +Wed May 15 14:26:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): If -d and CVS/Root both specified, after writing + the value from -d into CVS/Root, use the value from -d, not the + old value from CVS/Root. Don't write CVS/Root with value from -d + until we have verified that it works. + * sanity.sh: Reenable test basica-9 and adjust for new behavior. + +Tue May 14 1996 Jim Kingdon <kingdon@cyclic.com> + + * logmsg.c (do_editor): If user aborts the commit, still remove the + temporary file. + +Tue May 14 11:45:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * filesubr.c, cvs.h (cvs_temp_name): New function. Move L_tmpnam + define from cvs.h to filesubr.c. + * client.c, diff.c, import.c, login.c, logmsg.c, no_diff.c, + patch.c, wrapper.c: Call cvs_temp_name not tmpnam. + * login.c (login): Reindent function. + +Tue May 14 10:56:56 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_fast_checkout): If workfile is NULL, don't call chmod. + +Mon May 13 10:52:10 1996 Greg A. Woods <woods@most.weird.com> + + * checkout.c (export_usage): note which options cause a sticky + version to be set, and which option avoids this. + * update.c (update_usage): likewise + +Sat May 11 18:57:07 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Comment out test basica-9 until I get around to + actually fixing it (the -d vs. CVS/Root change broke it). + +Fri May 10 09:39:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): -d now overrides CVS/Root. + +Thu May 9 19:45:24 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c: Remove comment listing commands at beginning. It was + out of date and redundant with the help. + +Thu May 9 09:33:55 1996 Greg A. Woods <woods@most.weird.com> + + * main.c: add 'init' to opening comment listing commands + + * mkmodules.c (init): fix to recognize argc==-1 as hint to call + usage() [should make "cvs init -H" work as expected] + +Wed May 8 15:02:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Set EXPR in the case that the expr in the path is the + one that we want. + +Wed May 8 14:06:24 1996 Greg A. Woods <woods@most.weird.com> + + * sanity.sh (test): - convert all '[' to test ala GCD + +Wed May 8 13:46:56 1996 Greg A. Woods <woods@most.weird.com> + + * sanity.sh (expr): - make a valiant attempt to find GNU expr + - Patch from Larry Jones: + sanity test deep-4 failed with "expr: arg list too long" + sanity test 56 failed because the stderr and stdout output was not + interleaved as expected. + sanity test modules-155a4 failed with "ls: illegal option -- 1" + + * main.c (main): - Patch from Larry Jones for SysV setvbuf + +Tue May 7 16:41:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Increment version number to 1.8.2 to work around fact + that CVS 1.8 (confusingly) calls itself 1.8.1 not 1.8. + +Tue May 7 10:44:20 MET DST 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * rcs.c (rcsvers_delproc): fix memory leak by freeing author + field. + +Mon May 6 10:40:05 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (conflicts): New test conflicts-126.5 tests for bug + which Ian fixed May 5 in update.c + +Mon May 6 06:00:10 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * Version 1.8.1 + +Sun May 5 21:39:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * vers_ts.c (Version_TS): If sdtp is NULL, go ahead and check + RCS_getexpand for options. Fixes binaries and non-unix clients. + * sanity.sh: Fix binfiles-5.5 to test for the correct behavior + rather than the buggy behavior which existed when the binfiles-5.5 + test was written. + (binfiles-14c,binfiles-14f): Likewise. + +Sun May 5 17:38:21 1996 Benjamin J. Lee <benjamin@cyclic.com> + + Integrated changes submitted by Ian Taylor <ian@cygnus.com> + + * update.c (update_dirent_proc): cvs co -p doesn't print + anything when run from an empty directory. + + * import.c (import_descend_dir): Check for a file in the + repository which will be checked out to the same name as the + directory. + +Sat May 4 12:33:02 1996 Ian Lance Taylor <ian@cygnus.com> + + Extract the head revision directly from the RCS file when + possible, rather than execing co. + * rcs.c (RCS_reparsercsfile): Set delta_pos field. + (getrcskey): Add lenp parameter. Change all callers. + (RCS_fast_checkout): New function. + (annotate_fileproc): If PARTIAL is not set, just fseek to + delta_pos. + * rcs.h (struct rcsnode): Add delta_pos field. + (RCS_fast_checkout): Declare. + * diff.c (diff_file_nodiff): Call RCS_fast_checkout rather than + RCS_checkout. + * import.c (update_rcs_file): Likewise. + * no_diff.c (No_Difference): Likewise. + * patch.c (patch_fileproc): Likewise. + * update.c (checkout_file): Likewise. + (patch_file): Likewise. + (join_file): Likewise. + +Sat May 4 12:33:02 1996 Ian Lance Taylor <ian@cygnus.com> + + * classify.c (Classify_File): Don't report a conflict for a + pending remove if somebody else has already removed the file. + +Thu May 2 13:34:37 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * Version 1.7.88 + +Thu May 2 01:40:55 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * server.c (HAVE_INITGROUPS): Use initgroups() only if + located by configure, in the event a system has crypt(), but + no initgroups() + +Wed May 01 21:08:21 1996 noel <noel@BOAT_ANCHOR> + + * client.c (filter_through_gunzip): use "gzip -d" instead of + "gunzip," since there's no good reason (on NT at least) to have an + extra copy of gzip.exe copied to gunzip.exe (Arrrrgh! No symbolic + links!). + + * mkmodules.c (init): check to see that we have the correct number + of arguments or print out the usage message (used to be argc > 1, + should be argc != 1, because help forces argc == -1 as a special + case). + +Wed May 1 18:05:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (basica): When testing rejection of reserved tag name, + use BASE instead of RESERVED. + +Wed May 1 15:15:11 1996 Tom Jarmolowski <tjj@booklink.com> + + * rcs.c (linevector_delete): Only copy up to vec->nlines - nlines, + not to vec->nlines. + +Wed May 1 15:43:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcscmds.c (RCS_settag): Instead of reserving all tag names + containing only uppercase letters, reserve only BASE and HEAD. + * sanity.sh (mflag): Revert 26 Mar change; use all-uppercase tag + name again. + +Wed May 1 15:15:11 1996 Tom Jarmolowski <tjj@booklink.com> + + * rcs.c (linevector_add): Move increment of i out of larger + statement, to avoid assumptions about evaluation order. + +Tue Apr 30 15:46:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Version 1.7.87. + + * server.c (check_password): Don't use ANSI string concatenation. + Reindent function. + +Mon Apr 29 10:48:38 1996 Noel Cragg <noel@gargle> + + * root.c (parse_cvsroot): removed "rsh" as an alias to "server" in + the method section. + + * main.c (main): new variable help so we can support the `cvs -H + cmd' convention. Reverts change of 26 Apr 96 which removed this + feature. + +Sun Apr 28 14:57:38 1996 Noel Cragg <noel@gargle> + + * main.c (main): update error message if parse_cvsroot fails. + * server.c (serve_root): same. + (serve_init): same. + + * client.c (start_tcp_server): get rid of the "fall through" + stuff, now that we have access methods. + (start_server): switch off the access method to choose routine + that starts the server. + (start_tcp_server): tofd wasn't getting set to -1 early enough, + because a call to error for bind or gethostbyname might fail and + the subsequent error check to see if the connection had been made + would fail. + + * root.c: new variable method_names for error reporting purposes. + +Sun Apr 28 17:22:15 1996 Noel Cragg <noel@occs.cs.oberlin.edu> + + * server.c: moved kerberos #includes from main.c for the + kserver_authenticate_connection routine. + +Fri Apr 26 07:59:44 1996 Noel Cragg <noel@gargle> + + * server.c (serve_init): use the new return value from + parse_cvsroot. + (serve_root): same. + * main.c (main): same. + + * root.c (parse_cvsroot): fix indentation, add a return value + which tells whether the command succeeded or failed. + + * main.c (main): move the setting of the UMASK environment + variable inside the stuff that gets done if the user is NOT asking + for help, so we don't signal any errors prematurely (don't want to + give an error because we can't parse an environment variable + correctly if the user asks for help). Similar mods for the code + that tries to get the working directory. + + Also make CVSADM_Root a local variable instead of a global, since + its scope is only about 20 lines here! + + * server.c (kserver_authenticate_connection): moved code from + main.c to clean up MAIN. Makes sense, since we already have a + pserver_authenticate_connection. + (pserver_authenticate_connection): rename from + authenticate_connection. + + * main.c (main): reorganized the routine to eliminate variables + help, help_commands, and version_flag. Now the routine is much + clearer, since we don't have to be checking to see if these + variables are set. One behavior that was a bug/feature which is + now gone is an invocation like "cvs -H rtag" -- previously this + would give usage for rtag, but now gives usage for cvs itself. + The first behavior didn't make sense, especially since we say in + the docs that command-line flags are position-specific. *Reverted + Above* + +Thu Apr 25 20:05:10 1996 Noel Cragg <noel@gargle> + + * main.c (main): make sure we have a valid command name before we + do anything else (moved the thing that looks for a command in CMDS + to right after the GETOPT loop). Added `kserver' and `pserver' to + the table so they will be recognized; set their functions to + SERVER so that help will be given when asked for. + + * expand_path.c (expand_variable): return CVSroot_original rather + than CVSroot_directory. + + * main.c (main): save CVSroot in the env rather than + CVSroot_original, since we might not have called PARSE_CVSROOT + (this can happen if we use the -H option to a command). + + * root.c (parse_cvsroot): the parsing method was bogus for + guessing when we had hostnames vs. directories specified. Any + ambiguity should be removed by having the user specify the access + method. If the access method isn't specified, choose + server_method if the string contains a colon or local_method + otherwise. + + * Changed CVSroot_remote back to client_active since the code + reads better. + +Wed Apr 24 17:27:53 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * vers_ts.c (Version_TS): xmalloc enough space (1 more + byte). Thanks to purify! + +Mon Apr 22 00:38:08 1996 Noel Cragg <noel@gargle> + + * create_adm.c (Create_Admin): pass CVSroot_original instead of + CVSroot_directory (oops!). + * update.c (update_filesdone_proc): same. + + * server.c (serve_root): modify to use parse_cvsroot rather than + goofing around with other variables. Will need to fix + parse_cvsroot to have a return value so we can return an error and + quit gracefully if in server mode. + (serve_init): same. + + * main.c: modify command table to remove client_* routines, since + they no longer exist. + (main): don't try to switch off non-existent field in command + table! ;-) + + * client.h (client_*): removed prototypes for now non-existent + functions. + + * client.c: remove proto for get_cvs_password, since it is now in + cvs.h. Modify routines to use new globals that describe CVSROOT + rather than client_active, server_host, server_user, and + server_cvsroot. + (parse_cvsroot): removed function, since a more generic version + now lives in root.c. + (connect_to_pserver): remove call to parse_cvsroot, since main.c + has already done it for us. + (client_*): removed all of these routines, since they only call + parse_cvsroot and then their respective operation functions. + Since main.c has already called parse_cvsroot, we shouldn't bother + with the extra function call, since client-server diffs are + already handled in the core routines themselves. + + * main.c: remove CVSroot as a global variable. Remove + use_authenticating_server variable since we have a new + `CVSroot_method' variable instead. + (main): add `CVSroot' as a local variable. Call parse_cvsroot + after we're sure we have the right setting for `CVSroot.' + + * login.c (login): update to use new global variables. Instead of + old behavior which let the user type in user@host when prompted, + it makes them do it in CVSROOT proper. The routine still lets the + user type the password, however. + (get_cvs_password): make sure that CVSROOT is fully qualified + before trying to find the entry in the .cvspass file. + * cvs.h: add prototype for get_cvs_password. + + * add.c: use new globals that describe CVSROOT. + * admin.c: same. + * checkout.c: same. + * commit.c: same. + * create_adm.c: same. + * diff.c: same. + * edit.c: same. + * expand_path.c: same. + * history.c: same. + * ignore.c: same. + * import.c: same. + * log.c: same. + * mkmodules.c: same. + * modules.c: same. + * parseinfo.c: same. + * patch.c: same. + * rcs.c: same. + * recurse.c: same. + * release.c: same. + * remove.c: same. + * repos.c: same. + * rtag.c: same. + * status.c: same. + * tag.c: same. + * update.c: same. + * watch.c: same. + * wrapper.c: same. + + * root.c (Name_Root): remove error message that reports missing + CVSROOT, since new code in main.c will catch it and also print out + an error. + (parse_cvsroot): new function -- takes a CVSROOT string and breaks + it up into its component parts -- method, hostname, username, and + repository directory. Sets new global variables that describe the + repository location more precisely: CVSroot_original, + CVSroot_remote, CVSroot_method, CVSroot_username, + CVSroot_hostname, CVSroot_directory for use by all other + functions. Checks for obvious errors in format of string. + (main): a short routine to test parse_cvsroot from the command + line. + * cvs.h: add prototype for parse_cvsroot and extern definitions + for new globals. + + * cvs.h: removed CVSroot variable, since we don't want other + routines using the raw CVSROOT (also helped to find all of the + refs to the variable!). + +Fri Apr 19 11:22:35 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * Version 1.7.86 + +Thu Apr 18 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (try_read_from_server): Compare return value from fwrite + with a size_t not an int (Visual C++ lint). + +Wed Apr 17 11:56:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (try_read_from_server): New function. + (read_from_server): Use it. + (read_counted_file): New function. + * client.c, server.c: Add Template response. + * cvs.h (CVSADM_TEMPLATE): Added. + * logmsg.c (do_editor): If repository is NULL, use CVSADM_TEMPLATE + file in place of rcsinfo. + * server.c, server.h (server_template): New function. + * create_adm.c (Create_Admin): Call it. + +Tue Apr 16 13:56:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * repos.c (Name_Repository): Fix comments. + * create_adm.c (Create_Admin): Fix indentation. + +Wed Apr 10 16:46:54 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * options.h.in: Include relevant information here rather than + citing (former) FAQ. + + * ChangeLog-9395: Fix typo in introductory paragraph. + +Wed Apr 10 14:55:10 1996 code by Mike Spengler mks@msc.edu + comments by Jim Kingdon <kingdon@harvey.cyclic.com> + + * filesubr.c (unlink_file_dir,deep_remove_dir): Don't call unlink + on something which might be a directory; check using isdir instead. + +Wed Apr 10 14:55:10 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * checkout.c (build_dirs_and_chdir): Pass path, not cp, to + Create_Admin. The former is the correct update dir. + * sanity.sh (modules): New tests modules-155* test, for above fix. + +Mon Apr 8 13:53:27 1996 Samuel Tardieu <sam@inf.enst.fr> + + * rcs.c (annotate_fileproc): If the file is not under CVS control, + return instead of dumping a core. Don't bug on files with an empty + first revision. + +Fri Mar 29 16:08:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.c (annotate_fileproc): If last line of add-chunk is not + newline terminated, end the loop when we find that out. + +Fri Mar 29 16:59:34 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * rcs.c (annotate_fileproc): allow last line of add-chunk not to + be newline terminated + +Thu Mar 28 10:56:36 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + Add more diff tests: + * sanity.sh (basic2): Use dotest for test 61. + (basica): Add test basica-6.2. + (branches): Add tests branches-14.4 and branches-14.5. + (basic1): Remove tests 19, 20, 25, and 26. The only thing this + might miss out on is diff's interaction with added and removed + files, but those tests didn't test that very well anyway. + + * rcs.c (RCS_getrevtime): Add comment regarding years after 1999. + + * rcs.c: Add "cvs annotate" command and related code. + (getrcskey): Move special handling of RCSDESC from here to + callers. Handle those keys (desc, log, text) which do not + end in a semicolon. + * rcs.h (RCSVers): Add author field. + * rcs.c (RCS_reparsercsfile): Set it. + * cvs.h (annotate), main.c (cmd_usage, cmds), client.h client.c + (client_annotate), server.c (serve_annotate, requests): Usual + machinery to add a new command. + * sanity.sh (basica): Test cvs annotate. + + * sanity.sh (branches): More tests, of things like adding files on + the trunk after a branch has been made. + +Tue Mar 26 09:48:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * expand_path.c: Don't declare free and xmalloc; cvs.h already + takes care of that. + + * sanity.sh (mflag): Don't use tag name reserved to CVS. + + NT local changes plus miscellaneous things noticed in the process: + * import.c (add_rcs_file): Use binary mode to write RCS file. Use + \012 where linefeed is intended. Copy data a small block at a + time, until we hit EOF, rather than trying to read the whole file + into memory at once. + * client.c (send_modified): Add comments regarding st_size. + * commit.c (commit): Add comments regarding binary mode and read(). + * logmsg.c (do_editor): Add comments regarding st_size. + * server.c (server_updated): Use binary mode to read file we are + sending. + + * rcscmds.c (RCS_settag): Complain if user tries to add a tag name + reserved to CVS. + * sanity.sh (basica): Test for this behavior. + + * sanity.sh (binfiles): New tests test ability to change keyword + expansion. + +Mon Mar 25 1996 Jim Kingdon <kingdon@cyclic.com> + + * cvs.h, filesubr.c (expand_wild): New function. + * recurse.c (start_recursion): Call expand_wild at beginning and + free its results at the end. + * cvs.h, subr.c (xrealloc): Make argument and return value void *. + * client.h, client.c (send_file_names): Add flags argument. If + SEND_EXPAND_WILD flag is passed, call expand_wild at beginning and + free its results at the end. + * admin.c, add.c, log.c, tag.c, status.c, edit.c, watch.c, + update.c, commit.c, remove.c, client.c, diff.c: Update callers. + +Fri Mar 22 10:09:55 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * error.c (error, fperror): Exit with status EXIT_FAILURE rather + than STATUS. We had been neglecting to check for 256, and the + value of providing a count of errors is probably minimal anyway. + * add.c, modules.c, mkmodules.c, tag.c, server.c, main.c, + import.c, client.c, scramble.c, recurse.c: Exit with status + EXIT_FAILURE rather than 1. On VMS, 1 is success, not failure. + * main.c (main): Return EXIT_FAILURE or 0. The value of providing + a count of errors is minimal. + + * client.c (init_sockaddr): Exit with status 1 rather than + EXIT_FAILURE. The latter apparently doesn't exist on SunOS4. + Reindent function. + +Mon Mar 18 14:28:00 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.h, ignore.c: New variable ign_case. + * ignore.c (ign_name): If it is set, match in a case-insensitive + fashion. + * server.c (serve_case): New function. + (requests): Add Case request. + * client.c (start_server): If FILENAMES_CASE_INSENSITIVE is + defined, send Case request. + +Sat Mar 16 08:20:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + For reference, this change takes cvs's text segment from 315392 + bytes to 311296 bytes (one 4096 byte page). + * cvs.h (struct file_info): Add fullname field. + * recurse.c (do_file_proc): Set it. + * commit.c (find_fileproc), client.c (send_fileproc), commit.c + (check_fileproc), diff.c (diff_fileproc), edit.c + (unedit_fileproc), patch.c (patch_fileproc), remove.c + (remove_fileproc), rtag.c (rtag_fileproc), tag.c (tag_fileproc), + update.c (update_fileproc), watch.c (watchers_fileproc): Use it + instead of computing it each time. + * diff.c (diff_fileproc), remove.c (remove_fileproc): Use fullname + where we had been (bogusly) omitting the directory from user + messages. + * edit.c (unedit_fileproc, edit_fileproc): If we cannot close + CVSADM_NOTIFY, mention CVSADM_NOTIFY rather than finfo->file in + error message. + * rtag.c (rtag_fileproc), tag.c (tag_fileproc): Reindent. + +Fri Mar 15 15:12:11 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * server.h: fix prototype of server_pause_check (was + server_check_pause) + +Thu Mar 14 1996 Jim Kingdon <kingdon@cyclic.com> + + * vers_ts.c (Version_TS), entries.c (Scratch_Entry, AddEntryNode): + Change findnode to findnode_fn. + + * main.c: Depending on HAVE_WINSOCK_H, include winsock.h or + declare gethostname. + * cvs.h: Don't declare it here. + +Thu Mar 14 07:06:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (find_fileproc): If vn_user is NULL and ts_user is not, + print an error rather than silently succeeding. + * sanity.sh (basica-notadded): New test, for above fix. + (dotest_internal): New function. + (dotest,dotest_fail): Call it instead of duplicating code between + these two functions. + + * sanity.sh: Skip tests binfiles-9 through binfiles-13 for remote. + + * options.h.in: Adjust comment to reflect kfogel change. + +Thu Mar 14 01:38:30 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * options.h.in (AUTH_CLIENT_SUPPORT): turn on by default. + +Wed Mar 13 09:25:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * vers_ts.c (Version_TS): Don't try to override options from rcs + file if there isn't an rcs file (e.g. called from send_fileproc). + This fixes a bug detected by test 59 in "make remotecheck". + + * rcs.c (RCS_reparsercsfile, RCS_getexpand): Assert that argument + is not NULL. + + Fix a gcc -Wall warning: + * rcs.c, rcs.h (RCS_getexpand): New function. + * vers_ts.c (Version_TS): Call it. + * rcs.c (RCS_reparsercsfile): Make static. + + Add a "cvs init" command. This is needed because cvsinit.sh + invoked mkmodules which doesn't exist any more. + * mkmodules.c: Break filelist out of mkmodules function, rename + struct _checkout_file to struct admin_file (for namespace + correctness), and add contents field. + (init,mkdir_if_needed): New functions. + * cvs.h (init): Declare. + * main.c (cmds): Add init. + (main): If command is init, don't require cvsroot to exist. + * client.c, client.h (client_init, send_init_command): New functions. + * client.c (start_server): Don't send Root request if command is init. + * server.c (serve_init): New function. + (requests): Add "init". + +Wed Mar 13 09:51:03 MET 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * vers_ts.c (Version_TS): set options to default option if the + file if no -k option but -A was given. This avoids the (wrong) + update message for binary files which are up-to-date when + running 'cvs -A'. + + * update.c (checkout_file): remove test of -k option stored in the + file itself because it was moved to vers_ts.c + + * sanity.sh: added tests for the above fix. + +Tue Mar 12 13:47:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * hash.c (findnode): Adjust comment regarding errors. + + * hash.c (findnode, findnode_fn): Assert that key != NULL. This + way the check still happens even if the function is later + rewritten to not start out by calling hashp. + +Mon Mar 11 10:21:05 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: If expr accepts multi-line patterns but is too + liberal in matching them, print a warning but keep going. + + * sanity.sh: Add QUESTION variable, analogous to PLUS. Use it + instead of \? to match a question mark. + + * cvs.h (CVSMODULE_OPTS, CVSMODULE_SPEC): Move from here... + * modules.c: ...to here. They are only used here and the code to + handle the syntax of modules files should not be scattered all over. + * modules.c (CVSMODULE_OPTS): Add "+" as first character. + * sanity.sh (modules): New tests 148a0 and 148a1 test for + above-fixed bug. + +Mon Mar 11 13:11:04 1996 Samuel Tardieu <sam@inf.enst.fr> + + * modules.c (cat_module): set optind to 0 to force getopt() to + reinitialize its internal nextchar + +Mon Mar 11 00:09:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * hash.c (findnode, findnode_fn): Revert changes of 7-8 Mar 1996. + The correct style is to assert() that key != NULL (see HACKING), + which is already done in the hashp function. + * fileattr.c (fileattr_delproc): Likewise, assert() that + node->data != NULL rather than trying to deal with it being NULL. + +Fri Mar 8 01:31:04 1996 Greg A. Woods <woods@most.weird.com> + + * hash.c (findnode_fn): one more place to avoid calling hashp() + with a NULL key + +Thu Mar 7 17:30:01 1996 Greg A. Woods <woods@most.weird.com> + + * hash.c (findnode): also return NULL if key is not set + [[ reported by Chris_Eich@optilink.optilink.dsccc.com, and + supposedly in a PR that should be marked "fixed"..... ]] + + * fileattr.c (fileattr_set): set node->data to NULL after freeing + it to prevent subsequent accesses + (fileattr_delproc): don't free node->data if it's NULL, and set it + to NULL after freeing + [[ reported by Chris_Eich@optilink.optilink.dsccc.com, and + supposedly in a PR that should be marked "fixed"..... ]] + +Fri Mar 1 14:56:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (basica): New test basica-4a tests for bug fixed by + sam@inf.enst.fr on 1 Mar 96. + +Fri Mar 1 18:10:49 1996 Samuel Tardieu <sam@inf.enst.fr> + + * tag.c (check_fileproc): Check for file existence before trying + to tag it. + +Fri Mar 1 07:51:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (update_entries): If command is export, set options to + NULL. + +Thu Feb 29 16:54:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lock.c (write_lock, Reader_Lock): Remove + BOGUS_UNLESS_PROVEN_OTHERWISE code. It was pretty bogus, and has + been ifdeffed out for a long time. + * cvs.h (CVSTFL): Removed; no longer used. + + * cvsrc.c, cvs.h (read_cvsrc): Pass in command name rather than + using global variable command_name. + * main.c (command_name): Initialize to "", not "cvs" so that error + messages don't say "cvs cvs". Update calls to read_cvsrc to pass + in command_name or "cvs" as appropriate. + * sanity.sh (basica): New test basica-9 tests for above-fixed bug. + + * lock.c: Rename unlock to lock_simple_remove to avoid conflict + with builtin function on QNX. + +Thu Feb 29 17:02:22 1996 Samuel Tardieu <sam@inf.enst.fr> + + * fileattr.c (fileattr_get): Removed NULL pointer dereference + which occurred in the absence of default attribute. + +Thu Feb 29 07:36:57 1996 J.T. Conklin <jtc@rtl.cygnus.com> + + * rcs.c (RCS_isbranch, RCS_whatbranch): Remove no longer used file + argument, swap order of remaining two arguments to be like other + RCS_* functions. + (RCS_nodeisbranch): swap order of arguments to be like other RCS_* + functions. + * rcs.h (RCS_isbranch, RCS_whatbranch, RCS_nodeisbranch): Update + prototypes for above changes. + * commit.c, rtag.c, status.c, tag.c: Update for above calling + convention changes. + +Thu Feb 29 08:39:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_server): Revert changes which claimed to fall + back to a different way of connecting. Add comments explaining + why. (I don't think the changes did what they claimed, anyway). + Use indentation rather than comments to line up #if, #else, and + #endif. + + * patch.c (patch, patch_fileproc): Revert change to add optional + arguments to -c and -u. Optional arguments are evil and in + violation of the POSIX argument syntax guidelines. The correct + way to do this is -C and -U. Also change DIFF back to "diff" in + output (see comments). + + gcc -Wall lint: + * client.c (copy_a_file): Declare p inside the #ifdef in which is + it used. + * commit.c (remove_file): Remove unused variable p. + * commit.c (checkaddfile): Remove unused variables p. + * rcs.c (RCS_isbranch): Remove unused variable p. + * rcs.c: Remove unused declarations and definitions of + parse_rcs_proc, rcsnode_delproc, rcslist, and repository. + * rtag.c (rtag_fileproc): Remove unused variable p. + * patch.c (patch_fileproc): Remove unused variable p. + * tag.c (val_fileproc): Remove unused variable node. + * client.c, import.c, lock.c, server.c: Cast pid_t to long before + passing it to %ld. + + * cvs.h: Don't prototype gethostname; merely declare it (on linux, + second argument is size_t not int). + +Thu Feb 29 10:29:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de> + + * sanity.sh: added "cat > /dev/null" to loginfo entry to avoid the + SIGPIPE signal + +Thu Feb 29 10:28:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de> + + * patch.c: added new variable diff_opt + (patch): allow optional parameter to -c and -u option, send it to + server + (patch_fileproc): cleaned up the code which prints the current + filename. For "-s" option, print the pathname relative to CVSROOT + instead of just the filename. + + * filesubr.c (xchmod): added cast to shut up gcc + + * cvs.h: added prototype for gethostname + +Thu Feb 29 10:27:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de> + + * lock.c (write_lock), (Reader_Lock), import.c (update_rcs_file), + client.c (update_entries), (send_modified), server.c (server), + (receive_file), (server_updated): use %ld for printing pid_t + variables + +Thu Feb 29 02:22:12 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * run.c (run_exec): Added VMS return status support. + +Thu Feb 29 01:07:43 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * client.c (send_to_server): wrtn wasn't being declared under + VMS for some reason. + +Wed Feb 28 23:27:04 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * client.c: Changed #ifdef VMS && NO_SOCKET_TO_FD to + #if defined(VMS) && defined(NO_SOCKET_TO_FD) + +Wed Feb 28 22:28:43 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * build_src.com: Added DCL command procedure to build + and link CVS client for VMS. + +Wed Feb 28 22:07:20 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * client.c: VMS CVS client specific changes. + + Added USE_DIRECT_TCP to allow CVS_PORT to be used to specify + a TCP connection port (no Kerberos). Changed + start_kerberos_server() to start_tcp_server(). + + In copy_a_file(): transform a backup file to have a + VMS-friendly name. + + Added HAVE_CONFIG_H to include "config.h". + + start_server() will starts the first successful of any + mutually exclusive methods of starting the CVS server + which might be enabled. + + Initialized use_socket_style and server_sock for VMS in + start_server(). + +Wed Feb 28 21:49:48 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * find_names.c, recurse.c, cvs.h: Changed Find_Dirs() to + Find_Directories(). + * cvs.h: Added VMS filenames enabled through USE_VMS_FILENAMES + VMS POSIX will require to use the regular CVS filenames + while VMS is #define'd. + +Wed Feb 28 21:26:22 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * ignore.c: Added the patterns *.olb *.exe _$* *$ to default + ignore list for VMS. + +Wed Feb 28 13:32:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * logmsg.c (do_editor): Fix indentation. + +Wed Feb 28 12:56:49 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * logmsg.c (do_editor): If no editor is defined, exit and print + a message. + +Wed Feb 28 10:40:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * vers_ts.c (time_stamp, time_stamp_server): Reindent and revise + comments. + +Tue Feb 27 23:57:55 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * vers_ts.c: gmtime() returns NULL on some systems (VMS) + revert to local time via ctime() if GMT is not avaiable. + +Tue Feb 27 13:07:45 1996 J.T. Conklin <jtc@rtl.cygnus.com> + + The changes listed below cause cvs to parse each rcs file (and + free the associated rcsnode after the file has been processed) + sequentially. cvs used to parse all files in a directory, an + approach that does not scale to huge repositories with lots + of revisions/branches/tags/etc. + + * cvs.h (struct file_info): Removed srcfiles field. Added rcs + (node) field. + * recurse.c (do_recursion): Removed code that pre-parsed all + rcs files in the directory. + (do_file_proc): Parse current rcs file. + * rcs.c (RCS_parsefiles, parse_rcs_proc, RCS_addnode): Removed. + (RCS_isbranch, RCS_whatbranch): Changed srcfiles argument to + rcs (node). + * rcs.h (RCS_parsefiles, RCS_addnode): Removed prototypes. + (RCS_isbranch, RCS_whatbranch): Updated prototypes. + * add.c, admin.c, checkin.c, checkout.c, classify.c, client.c, + commit.c, diff.c, history.c, import.c, log.c, patch.c, remove.c, + rtag.c, status.c, tag.c, update.c, vers_ts: Updated for above + calling convention / data structure changes. + +Mon Feb 26 16:07:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Version 1.7.3. + + * Version 1.7.2. + +Mon Feb 26 1996 Jim Kingdon <kingdon@cyclic.com> + + * recurse.c (start_recursion): Use last_component rather than + checking for '/' directly. + (do_dir_proc): Likewise. + + Visual C++ lint: + * client.c (send_to_server): Change wrtn to size_t. + (connect_to_pserver): Put tofd and fromfd declarations inside + #ifndef NO_SOCKET_TO_FD. + * scramble.c (shifts): Change from array of char to array of + unsigned char. + +Mon Feb 26 13:31:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (check_repository_password): Remove unused variables + linelen, ch. + + * client.c (send_file_names): Translate ISDIRSEP characters to '/'. + +Sat Feb 24 21:25:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * checkout.c (safe_location): Re-indent one line. + +Sat Feb 24 10:50:42 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * checkout.c (safe_location): put assignment to hardpath[x] in an + `else'-clause, so we don't do it when x == -1. + +Sat Feb 24 01:40:28 1996 Marcus Daniels <marcus@sayre.sysc.pdx.edu> + via Karl Fogel <kfogel@floss.red-bean.com> + + * server.c (check_repository_password): Return by reference an + optional username, the `host_user', from the passwd file. The + host_user will be the user-id under which the cvs repository is + run. + (check_repository_password): Use `read_line' instead of fgets to + allow for passwords larger than 32 characters, as well as the + optional host user argument. + (check_password): Modify to use host_user. + (authenticate_connection): Modify to use host_user. + +Sat Feb 24 01:05:21 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * scramble.c (descramble): just shift descrambled string to get + rid of tag char, instead of allocating a whole new copy. + (scramble): cast return value of xmalloc to avoid unsightly + compiler warnings. + + * options.h.in (RCSBIN_DFLT): don't refer to AUTH_SERVER_SUPPORT + in comment anymore, now that it's not defined in this file. + +Fri Feb 23 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c: Ifdef HAVE_WINSOCK_H, include winsock.h + instead of sys/socket.h and friends. + * login.c: Don't include sys/socket.h and friends. + * login.c (login): Only fclose fp in the case where it was + successfully fopen'd. + * login.c: Declare getpass. + * filesubr.c, cvs.h (get_homedir): New function. + * cvsrc.c, expand_path.c, history.c, login.c: Call it instead + of getenv ("HOME"). + +Fri Feb 23 09:23:20 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (connect_to_pserver): Remove unused variable host. + * login.c: Include getline.h. + (login): Remove unused variables i and username. + (get_cvs_password): Move free of linebuf to where it actually will + be called. Add a "return NULL" at the end of the function to shut + up gcc -Wall. + + * options.h.in: Remove AUTH_SERVER_SUPPORT. + * client.h (authenticate_connection): Declare. + * scramble.c (scramble): Cast char to unsigned char before using + it to look up in table (char might be signed). + * server.c [AUTH_SERVER_SUPPORT]: Include grp.h + (authenticate_connection): Remove unused variables len and + server_user. + + * sanity.sh (basica): Add comments regarding creating a top-level + directory. + (basic1): Don't try to remove first-dir and + ${CVSROOT_DIRNAME}/first-dir at start of test; tests are now + responsible for cleaning up at the end. + (PLUS,DOTSTAR,ENDANCHOR): Add comments regarding fixed GNU expr. + +Thu Feb 22 22:34:11 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * cvs.h: Remove alloca cruft. + +Wed Feb 21 07:30:16 1996 J.T. Conklin <jtc@rtl.cygnus.com> + + * modules.c (do_module): call free_cwd before exiting. + + * recurse.c: Removed entries global variable. + (do_recursion): Declare entries. Moved call to Entries_Close so + entries list is closed on all code paths. + (start_recursion): Removed call to Entries_Close, entries list has + been moved to do_recursion only. + +Tue Feb 20 22:10:05 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (update_dirent_proc): If dir lacks a CVS subdirectory, + don't recurse into it. + * sanity.sh (conflicts): Test for above-fixed bug. + + * update.c (merge_file): Use write_letter not printf. + +Tue Feb 20 12:34:07 EST 1996: Gary Oberbrunner <garyo@avs.com> + and Jim Kingdon <kingdon@cyclic.com> + + * history.c (history_write): Change username to char * and call + getcaller() to set it. Setting username accidentally got deleted + 8 Feb 96. + * sanity.sh: Revise test 64 to test for above-fixed bug. + * sanity.sh (PLUS): New variable, work around yet another GNU expr + bug. + +Tue Feb 20 14:07:50 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Merge test rtags into test basic2. They never were + capable of running separately of each other. + + * sanity.sh (deep): New test, to test ability to operate in deeply + nested directories (more quickly than basic2 test did). + (basic2,rtags): Remove directories dir3 and dir4. Remove file8, + file10, file12, file9, file11, file13, file15, file16, file17. + These additional files slowed down the tests considerably without + significantly increasing coverage. + + * sanity.sh (PROG): New variable. Use it instead of "cvs" + to match the name cvs prints out for itself. + +Mon Feb 19 09:00:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + This fixes a bug whereby old default attributes would get + clobbered instead of added to on "cvs watch add". + * hash.c (findnode): Don't check for key == NULL; let the + assertion in hashp take care of it. + * fileattr.h, fileattr.c (fileattr_get): If filename is NULL, + return default attributes. + + * client.c (send_repository): Fix indentation. + +Mon Feb 19 01:10:01 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * login.c (login): print out full repos so user knows which server + she's logging into. + + * client.c (send_repository): die if `repos' is NULL. This is a + lame solution; see comments in code. + +Thu Feb 15 15:04:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * error.c (error): Free entire and mess when done with them. + + * sanity.sh (info): Correct syntax of .cvsrc file. + + * cvs.h, expand_path.c, edit.c, parseinfo.c, wrapper.c: + expand_path now takes arguments containing file and line for error + message, and it prints the error message itself. + * sanity.sh (info-6a): Test printing of error message. + + * expand_path.c (expand_variable): Add USER internal variable. + * sanity.sh (info): Test USER and CVSROOT internal variables too. + +Wed Feb 14 19:11:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (usg): Add -s option. + +Tue Feb 13 20:26:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + gcc -Wall lint: + * mkmodules.c (mkmodules_usage): Remove declaration of + non-existent function. + * cvs.h (mkmodules): Declare. + +Mon Feb 12 12:20:04 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * mkmodules.c: Rename main to mkmodules and remove various pieces + of scaffolding which it used to emulate non-existent parts of CVS. + Change calling convention to just take a char * not argc,argv. + Save and restore working directory. + * commit.c (commit_filesdoneproc): Call it if checking files into + CVSROOT. + * Makefile.in (SOURCES): Add mkmodules.c. + (OBJECTS): Add mkmodules.o. + (MSOURCES,MOBJECTS): Removed. + (COMMON_OBJECTS): Removed; move former contents into OBJECTS. + Update other rules accordingly. + * sanity.sh: Adjust to reflect nonexistence of mkmodules. + + These changes introduce functions cvs_output and cvs_outerr; + eventually all server output will go through them rather than + stdio directly. + * server.c (saved_output, saved_outerr): New variables. + (do_cvs_command): Initialize them. + (buf_output): Don't require that buf->output be set; saved_* use + this to shove some data in a buffer which buf_copy_lines will + later want to get data from. + * server.c, cvs.h (cvs_output, cvs_outerr): New functions. + * mkmodules.c (cvs_outerr): New function, so error() works. + * error.c: Reindent. Don't declare program_name and command_name; + cvs.h declares them. + (error): Use vasprintf and cvs_outerr (or fputs in the + error_use_protocol case) rather than stdio directly. + * import.c (import_descend_dir): Remove kludge which had prevented + messages from error() from being out of order with respect to + messages from printf; cvs_output and cvs_outerr are a cleaner + solution to the problem. + (add_log, import): Use cvs_output not printf. + * update.c (write_letter): Use cvs_output not printf. + (checkout_file): Use write_letter not printf. + * sanity.sh: Use dotest for test 56 (test that output is actually + correct). In theory should test that the import.c bug is fixed, + but I was unable to reproduce the bug (it is timing dependent). + +Mon Feb 12 16:07:45 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * commit.c: define last_register_time + (commit): make sure cvs doesn't exit in the same second it wrote + the last timestamp + (commit_fileproc): set last_register_time + (finaladd): set last_register_time + + * run.c, cvs.h: Changed more Popen() to run_popen() + +Mon Feb 12 03:06:50 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * release.c, rtag.c, tag.c: changed 'delete' to 'delete_flag' + to avoid symbol collision with DEC C RTL function delete() + +Mon Feb 12 03:01:48 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * mkmodules.c: changed 'void Lock_Cleanup()' to 'void static + Lock_Cleanup() to avoid conflict with more substantial + Lock_Cleanup() in lock.c + +Mon Feb 12 02:50:19 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * edit.c, logmsg.c, release.c, run.c: Changed Popen() to + run_popen(). VMS' linker is not case sensitive and considered + popen() and Popen() to be identical symbols. + +Sun Feb 11 10:51:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main) [!CLIENT_SUPPORT]: Silently ignore gzip level + rather than printing usage message. + + * cvs.h, expand_path.c (variable_list): New variable. + (variable_set): New function. + * hash.h (enum ntype), hash.c (nodetypestring): Add VARIABLE. + * expand_path.c (expand_path, expand_variable): Reindent. + (expand_variable): Use user variables not environment variables + for ${=VAR} syntax. The environment variables didn't work + client/server. + * main.c (main): Process new -s global option. + * client.c (send_variable_proc): New function. + (start_server): Call it, to send user variables. + * server.c (serve_set): New function. + (requests): Add Set request. + * sanity.sh: Revise info test to use user variables rather than + environment variables. + +Sat Feb 10 16:55:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + By itself this is only a small cleanup, but in the long run it + will be way cool (for reference, it takes CVS's text segment from + 290816 bytes to 294912, which I expect will be made up by future + changes which this enables): + * cvs.h (struct file_info): Added. + (FILEPROC): Replace 5 args with single struct file_info *. + * recurse.c (do_file_proc): Adjust args to fileproc; passed in + instead of from globals. + (do_recursion): Call do_file_proc accordingly. Remove srcfiles + global variable. + * update.c (update_fileproc): Renamed from update_file_proc. + * admin.c, client.c, commit.c, diff.c, edit.c, log.c, patch.c, + remove.c, rtag.c, status.c, tag.c, update.c, watch.c: Update + fileprocs to new calling convention. + +Fri Feb 9 15:30:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * expand_path.c (expand_variable): Accept a variable name starting + with '=' as a way to specify an environment variable. + * sanity.sh (info): New tests, for above behavior. + + * Makefile.in (clean): Also remove check.log check.plog. + + * import.c (comtable): Remove SYSTEM_COMMENT_TABLE; the table + should *not* depend on what kind of machine the server happens to + be. Add "mak", "rc", "dlg", "frm", and "bas" types which were + formerly included via SYSTEM_COMMENT_TABLE. + + * cvs.h, rcs.h, add.c, checkin.c, classify.c, commit.c, diff.c, + import.c, patch.c, rcs.c, update.c, vers_ts.c: Remove + DEATH_SUPPORT ifdefs. They were introduced to facilitate merging + between Cygnus and Berliner variants of CVS, not because it was + intended to subset CVS this way. And they clutter up the code + quite a bit. + * cvs.h, create_adm.c, main.c, update.c: Likewise, remove + CVSADM_ROOT ifdefs (it is still a #define, of course). I believe + they had a more-or-less similar motivation. + + * sanity.sh: Move setting of HOME from ignore test to the start of + the tests so it applies to all tests. + (CVS): Remove -f; the above change takes care of it. + + * rcs.h (RCS_MERGE): Removed; unused. + + * commit.c (checkaddfile): Fix memory leak. + + * admin.c, commit.c, diff.c, log.c, mkmodules.c: Pass -x,v/ to RCS + commands. + + * rcscmds.c, cvs.h (RCS_checkin): New function. + * checkin.c, commit.c, import.c: Call it, rather than run_*. + * cvs.h, commit.c: Remove DEATH_STATE define; the behavior + which used to be the default (DEATH_STATE) is now the only one. + Failing to define DEATH_STATE has been commented as obsolete at + least since CVS 1.5. We still can read repositories created with + such a CVS, however. + * rcs.h, rcs.c: Adjust comments regarding DEATH_STATE. + * subr.c (make_message_rcslegal): Add comment, describing + allocation of returned value. + +Fri Feb 9 09:53:44 MET 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * sanity.sh: use "${testcvs}" instead of "cvs" in devcom tests + + * hash.c: fix "dereferencing a NULL pointer" bug triggered with + "cvs watch add" + (findnode): return NULL if key == NULL + (hashp): assert (key != NULL) + +Fri Feb 9 00:46:47 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.c (RCS_reparsercsfile): Remove unused variable date. + + * myndbm.c (mydbm_load_file): Fix typo ('015' -> '\015'). + +Thu Feb 8 13:00:00 1996 Jim Kingdon <kingdon@peary.cyclic.com> + + * rcs.c (RCS_parse, RCS_parsercsfile, RCS_reparsercsfile), + fileattr.c (fileattr_read), myndbm.c (myndbm_open): + Use FOPEN_BINARY_READ. + * fileattr.c (fileattr_write), myndbm.c (myndbm_close): + Use FOPEN_BINARY_WRITE. + * history.c (history_write, read_hrecs): Specify OPEN_BINARY. + * rcs.c: Remove calls to abort. + * myndbm.c (myndbm_load_file): Ignore CRs from ends of lines + if present. + * myndbm.c, fileattr.c: While I am at it, change \n to \012 + a few places where LF is intended. + * history.c (history_write): Use getenv ("HOME"), not getpwnam, + to find home directory. If it isn't set, just keep going; don't + print a message. + * rcscmds.c, cvs.h (RCS_checkout): New function. + * update.c, checkin.c, commit.c, diff.c, import.c, no_diff.c, + patch.c: Call it instead of run_*. + * patch.c (patch_fileproc): Clean up inconsistent handling of + noexec flag. + * rcscmds.c (RCS_*): Pass -x,v/ to RCS commands; elsewhere in + CVS it is assumed that ,v is a suffix. + +Fri Feb 2 14:07:32 1996 J.T. Conklin <jtc@rtl.cygnus.com> + + * rcs.h (struct rcsnode): Remove dates field (list of rcsversnodes + indexed by date). CVS maintained this list for each RCS file even + though it was never used. This resulted in higher then necessary + memory requirements (and run time too). Even if revision info was + needed, CVS' List data structure is inappropriate because can't + handle duplicate keys. The above was discovered by tracking down + a memory leak. + * rcs.c (RCS_reparsercsfile): Don't build dates list. + (freercsnode): Don't delete dates list. + (rcsvers_delproc): Free date field. + (null_delproc): Removed. + +Thu Feb 1 12:28:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * remove.c (cvsremove): Don't tell user the name of the program + which they use to remove files; we don't have any way of knowing + that, and besides which they might use a GUI or emacs 'dired' anyway. + * update.c (update_filesdone_proc, update_dirleave_proc): Call + unlink_file_dir instead of rm -rf. + * options.h.in: Remove RM; no longer used. + + * sanity.sh: New tests devcom-a* test "cvs watch add", + "cvs watch remove", and "cvs watchers". + + * sanity.sh: New test 171a0 tests for watch.c bug just fixed by kfogel. + + * Most .c files: Remove rcsids. + * cvs.h: Remove USE macro. + +Thu Feb 1 13:07:15 1996 J.T. Conklin <jtc@rtl.cygnus.com> + + * tag.c, rtag.c: Update various comments to reflect function name + changes. + +Thu Feb 1 14:14:31 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * recurse.c (do_recursion): comment #endif. + + * edit.c (notify_check): surround with #ifdef CLIENT_SUPPORT; else + CVS won't compile if CLIENT_SUPPORT is undefined. + + * edit.h (notify_check): surround declaration with #ifdef + CLIENT_SUPPORT. + + * watch.c (watch): if argc <= 1, then just give usage (previously + was "argc == -1"). + +Thu Feb 1 12:28:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * README-rm-add: Remove information which is now in cvs.texinfo. + + * sanity.sh: Remove basic0 tests. Move a few to new tests + basica-1a* (but there is no need to test that *every* command + gracefully does nothing on an empty directory; exhaustive testing + is impractical and the generic recursion processor handles this + anyway). + + * sanity.sh: New tests 69a* test use of update -p to restore old + version of dead file. + +Wed Jan 31 18:32:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * ChangeLog-9395: Remove duplicate entries from 1996 which + accidentally got into this file. + + * client.c (read_line, read_from_server): Change "premature end of + file from server" message to "end of file from server (consult + above messages if any)" because 99% of the time it means rsh has + printed an error message and exited. + +Wed Jan 31 15:09:51 1996 J.T. Conklin <jtc@rtl.cygnus.com> + + * edit.c (ncheck_fileproc): Fix memory leak; free line before + returning. + +Tue Jan 30 18:06:12 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * recurse.c (do_recursion): Add comment about the fact that we + don't have locks in place at certain points. + +Tue Jan 30 09:43:34 1996 Vince Demarco <vdemarco@bou.shl.com> + + * edit.c (notify_proc): have notify_proc call expand_path with + the name of the filter program. The user may have used a + cvs environmental variable. (Popen will expand it, but it may not + use the correct value) + +Tue Jan 30 09:43:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * ChangeLog: take the pre-1996 changes and put them in a new file + ChangeLog-9395. + * ChangeLog-9194: Renamed from ChangeLog.fsf. + * ChangeLog-9194, ChangeLog-9395, ChangeLog: Add additional text + explaining the difference between all these logs and pointing to + older logs. + * Makefile.in (DISTFILES): Add ChangeLog-9194 and ChangeLog-9395; + remove ChangeLog.fsf. + + * modules.c (do_module): Don't fall through from 'l' to 'o' case + of option processing switch statement. + +Tue Jan 30 06:50:19 1996 J.T. Conklin <jtc@rtl.cygnus.com> + + * client.c (send_repository): Fix memory leak; free adm_name + before returning. + * diff.c (diff_file_nodiff): Fix memory leak; free xvers before + returning. + * rtag.c (rtag_fileproc): Fix memory leak; if branch_mode is set, + free rev before returning. + * status.c (status_fileproc, tag_list_proc): Fix memory leak; free + return value of RCS_whatbranch. + * tag.c (tag_fileproc): Fix memory leak; free vers before + returning. + (val_fileproc): Fix memory leak; free return value of RCS_gettag. + * watch.c (watch_modify_watchers): Fix memory leak; free mynewattr + before returning. + +Tue Jan 30 09:43:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lock.c (readers_exist): If stat gave an error, print an error + message saying it was from stat, rather than from "reading + directory". Skip the message completely if it was an + existence_error. + + * sanity.sh (branches): New tests (branches off of branches, etc.). + +Tue Jan 30 11:55:34 MET 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c (main): Add change to run getopt_long twice again. + +Mon Jan 29 15:59:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + gcc -Wall lint: + * client.c: Include edit.h + +Sun Jan 28 09:45:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * edit.c, edit.h (mark_up_to_date): New function, to remove file + in CVS/Base. + * client.c (update_entries): Call it if file is up to date. + * checkin.c (Checkin): Call it in non-server (local) case. + * sanity.sh: New test 182.5, tests for above-fixed bug. + +Sun Jan 28 01:07:22 1996 Jim Kingdon (kingdon@beezley) + + * client.c (change_mode): Separate out CHMOD_BROKEN code to parse + mode_string, rather than going through a mode_t. Cleaner than + the previous CHMOD_BROKEN code (which also had a typo of && not &). + +Sat Jan 27 23:29:46 1996 Jim Kingdon (kingdon@beezley) + + * edit.c (edit_fileproc): Check for EACCESS as well as EEXIST. + +Sat Jan 27 16:26:30 1996 Karl Fogel (kfogel@floss.cyclic.com) + + * client.c (notified_a_file): use rename_file() instead of + rename() (but temporarily set `noexec' to 0 so it runs + unconditionally). + (change_mode): deal with CHMOD_BROKEN. + +Fri Jan 26 00:14:00 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * server.c: renamed `dirname' to `dir_name', to avoid conflicts + with system headers. + + * client.c: renamed `dirname' and `last_dirname' to `dir_name' and + last_dir_name' (see above). Not strictly necessary, but + consistency is nice -- as long as you do it all the time. + +Thu Jan 25 00:41:59 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * options.h.in (AUTH_SERVER_SUPPORT, AUTH_CLIENT_SUPPORT): change + comment now that no longer under construction. + +Wed Jan 24 15:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Version 1.7.1. + + * Version 1.7. + +Sat Jan 20 00:05:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Version 1.6.87. + +Mon Jan 15 18:14:55 1996 Gary Oberbrunner <garyo@avs.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * tag.c (val_direntproc): New function to ignore + nonexistent dirs when recursing to check tag validity. + (tag_check_valid): Pass it to start_recursion. + * sanity.sh (death): New tests 65a0-65a6 cause test 74 to test for + above-fixed bug. + +Mon Jan 15 12:55:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c: Revert change to run getopt_long twice. This can go in + after 1.7. + +Mon Jan 15 13:03:28 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * filesubr.c (deep_remove_dir): added test of EEXIST for nonempty + directory (Posix states that both ENOTEMPTY (BSD) and EEXIST + (SYSV) are valid) + + * main.c (main): run getopt_long twice to allow command-line + suppression of reading the cvsrc file + +Fri Jan 12 10:02:43 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Version 1.6.86. + +Thu Jan 11 23:28:05 1996 J.T. Conklin <jtc@rtl.cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * fileattr.h (fileattr_startdir): Add comment about REPOS == NULL. + * fileattr.c (fileattr_read, fileattr_write): Assert that + fileattr_stored_repos != NULL. + (fileattr_free): If fileattr_stored_repos is NULL, don't free it. + +Thu Jan 11 18:03:21 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * scramble.c (descramble): deal with DIAGNOSTIC better. + +Thu Jan 11 12:04:42 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c: remove CVS_NOADMIN. + + * options.h.in: remove CVS_NOADMIN + +Thu Jan 11 10:28:44 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * scramble.c (descramble): make sure the string returned is safe + to free(). + +Wed Jan 10 01:11:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (serve_notify): Cast return value from malloc. + + * edit.c (notify_do): Use struct assignment, not struct + initialization (which SunOS4 /bin/cc doesn't have). + +Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Version 1.6.85. + + We use version numbers instead of patchlevels. But there was some + confusing patchlevel stuff lying around. Nuke it: + * Makefile.in (HEADERS): Remove patchlevel.h + * patchlevel.h: Removed. + * main.c: Don't include patchlevel.h. + (main): Don't print patch level. + + * server.c (check_repository_password): Check for errors from + system calls; reindent function. + +Tue Jan 9 23:15:30 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * expand_path.c: fix comments (explain expand_path()'s behavior + correctly). + +Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * edit.c (notify_proc): After copying in string following %s, + don't clobber it. Instead set up q to end of string. + + * watch.c (watch_modify_watchers), edit.c (editor_set): Fix sense + of test in trying to decide whether attributes are changed. + + * cvs.h (CVSROOTADM_USERS): New macro. + * edit.c (notify_do): Look up notifyee in CVSROOTADM_USERS if it + exists. + +Tue Jan 9 21:39:45 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * expand_path.c: don't redundantly #include things that cvs.h + already #includes (i.e., stdio.h, ctype.h, string[s].h). + +Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * ignore.c (ign_default): Add *.obj. + + * server.c: Put /* */ around #endif comment. + +Mon Jan 8 20:37:17 1996 Karl Fogel <kfogel@floss.red-bean.com> + + * client.c (connect_to_pserver): check return value of recv(). + +Mon Jan 8 11:37:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (connect_to_pserver): Check for error from connect; + reindent function. + + * sanity.sh (4.75): Use dotest, so we get a PASS if test passes. + + * sanity.sh (dotest): New argument OUTPUT2. + (188a): Use it instead of \|. + + * sanity.sh (import): Avoid using string $ followed by Id followed + by $ in sanity.sh source, in case sanity.sh itself is under CVS. + I hate keyword expansion. + + * sanity.sh: If expr cannot handle multiline expressions, fail and + tell the user to get one which can. + + * release.c (release_delete): Remove unused variable retcode. + +Fri Jan 5 13:30:00 1996 Jim Kingdon <kingdon@peary.cyclic.com> + + * release.c (release_delete): Call unlink_file_dir rather + than "rm -rf". + +Thu Jan 4 09:58:30 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (find_fileproc): Print "nothing known about foo" and + return 1 if the file doesn't exist and isn't in CVS/Entries. + (commit): If the recursion over find_fileproc returns an error, + print "correct above errors first!" just like local CVS. + * sanity.sh (basica): Test for above-fixed bug. + + * release.c (release): If we are the client, only unedit if the + server supports it. + + * sanity.sh: Remove STARTANCHOR stuff; expr patterns are + automatically anchored to the start. ENDANCHOR remains. + + * commit.c (commit): Don't start the server until we have + determined that there is something to commit. + +Thu Jan 4 09:48:33 1996 Ben Laurie <ben@gonzo.ben.algroup.co.uk> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_server): dup the file descriptor before + fdopening it. + +Wed Jan 3 18:25:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Remove tests 5, 5.5, and 5.75. All that stuff is + tested elsewhere. + + * ignore.c (ign_default): Change CVS* to CVS CVS.adm. CVS* is too + broad, especially in a case-insensitive filesystem. + + * Makefile.in (cvsbug): version.c is in srcdir. + +Wed Jan 3 17:30:45 1996 Phi-Long Tran <ptran@autodesk.com> + + * modules.c (do_module): Honor error_use_protocol in printing trace. + * server.c (server_register): Move check for options NULL to above + printing of the trace. + +Wed Jan 3 01:19:53 1996 Mark Immel <immel@centerline.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (checkout_file): Do not resurrect file on join if it + doesn't contain the revisions we are joining. Probably not a + perfect test, but should be an improvement. + * sanity.sh (death): New death-file4-* tests, for bug fixed above. + +Wed Jan 3 01:19:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c, admin.c, checkout.c, client.c, commit.c, diff.c, edit.c, + history.c, import.c, log.c, patch.c, release.c, remove.c, rtag.c, + status.c, tag.c, update.c, watch.c: In calling send_to_server, + pass \012 not \n. On the Mac \n is CR, not LF, and we want to + send LF. I didn't try to deal with whether files in CVSADM should + contain CR or LF--in fact there is some code in client.c which + reads \n from CVSADM files and passes it to send_to_server; it + needs to be cleaned up one way or the other. + + * entries.c (Entries_Open): Don't try to close fpin twice. + + * client.c (update_entries): Fix typo ("strlen (filename + 10)" + -> "strlen (filename) + 10"). + + * commit.c (checkaddfile): Remove arbitrary limit. + +Tue Jan 2 11:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (commit): Only pass files which were modified, added, + or removed to send_file_names. This has as a side effect a + semantic change--the up-to-date check is now skipped for other + files--but probably a good one, or at least not a bad one. + * sanity.sh (basica): New test; tests for bug fixed above. + * sanity.sh (187a3): Adjust for new 'cvs commit' output. Set up + DOTSTAR to match arbitrary text (another GNU expr bug/misfeature, + sigh). + + * sanity.sh: Test that the commit in test 43 actually worked. + Merge tests basic2 and basic3 and make them independent of basic1. + (pass,fail): Don't insert spurious space. + (45.5): Fix typo in directory name. + +Tue Jan 2 13:00:00 1996 Jim Kingdon <kingdon@peary.cyclic.com> + + Visual C++ lint: + * myndbm.c: Prototype write_item. + +Tue Jan 2 11:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + gcc -Wall lint: + * client.c (client_expand_modules): Pass error message not "" to error. + * client.c (supported_request), server.c (supported_response): + Return a value (gcc -Wall can't know that error doesn't return). + * commit.c (copy_ulist): Return a value. + * history.c (fill_hrec): Don't make assumptions about whether + time_t is "int" or "long" or what. + * cvs.h: Declare link_file. + * server.c: Include fileattr.h. + * server.c (server_notify): Remove unused variable val. + * tag.c (val_fileproc): Remove unused variable foundtag. + +Mon Jan 1 09:49:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Version 1.6.5. + + * Version 1.6.4. + + * filesubr.c (link_file): Add comment about link vs. copy semantics. + + * cvs.h (struct vers_ts): Fix comments. + * commit.c (commit): Before we ask for a log message, figure out + what is modified and what is not and pass the information to + do_editor. + (copy_ulist,find_fileproc): New helper functions for above code. + + * client.c (read_line): When writing to from_server_logfile, write + the \n too. + + * client.c (send_files): No longer call send_file_names. + * client.h: Update comment. + * add.c, admin.c, commit.c, diff.c, edit.c, log.c, remove.c, + status.c, tag.c, update.c, watch.c: Call send_file_names before + send_files. + * client.c: New variables module_argc, module_argv. + (client_expand_modules): Set them, to arguments. + (client_send_expansions): Use them instead of modules_vector to + send arguments. + * sanity.sh (modules): Add test of modules -d flag. + + +For older changes see ChangeLog-9395. diff --git a/contrib/cvs/src/Makefile.in b/contrib/cvs/src/Makefile.in index acb3343..f02edf2 100644 --- a/contrib/cvs/src/Makefile.in +++ b/contrib/cvs/src/Makefile.in @@ -12,12 +12,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# $CVSid: @(#)Makefile.in 1.19 94/09/29 $ - SHELL = /bin/sh srcdir = @srcdir@ @@ -43,31 +37,31 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ LIBS = @LIBS@ -SOURCES = add.c admin.c checkin.c checkout.c classify.c client.c commit.c \ -create_adm.c cvsrc.c diff.c edit.c entries.c error.c expand_path.c \ +SOURCES = add.c admin.c buffer.c checkin.c checkout.c classify.c client.c \ +commit.c create_adm.c cvsrc.c diff.c edit.c entries.c error.c expand_path.c \ fileattr.c find_names.c hash.c history.c ignore.c import.c \ lock.c log.c login.c logmsg.c main.c mkmodules.c modules.c myndbm.c no_diff.c \ parseinfo.c patch.c rcs.c rcscmds.c recurse.c release.c remove.c repos.c \ root.c rtag.c scramble.c server.c status.c subr.c filesubr.c run.c \ -tag.c update.c watch.c wrapper.c vers_ts.c version.c +tag.c update.c watch.c wrapper.c vers_ts.c version.c zlib.c -OBJECTS = add.o admin.o checkin.o checkout.o classify.o client.o commit.o \ -create_adm.o cvsrc.o diff.o edit.o entries.o expand_path.o \ +OBJECTS = add.o admin.o buffer.o checkin.o checkout.o classify.o client.o \ +commit.o create_adm.o cvsrc.o diff.o edit.o entries.o expand_path.o \ fileattr.o find_names.o hash.o history.o ignore.o import.o \ lock.o log.o login.o logmsg.o main.o mkmodules.o modules.o myndbm.o no_diff.o \ parseinfo.o patch.o rcs.o rcscmds.o recurse.o release.o remove.o repos.o \ root.o rtag.o scramble.o server.o status.o tag.o update.o \ watch.o wrapper.o vers_ts.o \ -subr.o filesubr.o run.o version.o error.o +subr.o filesubr.o run.o version.o error.o zlib.o -HEADERS = cvs.h rcs.h hash.h myndbm.h \ +HEADERS = buffer.h cvs.h rcs.h hash.h myndbm.h \ update.h server.h client.h error.h fileattr.h edit.h watch.h TAGFILES = $(HEADERS) options.h.in $(SOURCES) -DISTFILES = .cvsignore Makefile.in ChangeLog ChangeLog-9395 ChangeLog-9194 \ - NOTES README-rm-add \ - sanity.sh cvsbug.sh $(TAGFILES) +DISTFILES = .cvsignore Makefile.in \ + ChangeLog ChangeLog-96 ChangeLog-9395 ChangeLog-9194 \ + sanity.sh cvsbug.sh $(TAGFILES) build_src.com PROGS = cvs cvsbug @@ -91,8 +85,7 @@ saber_cvs: lint: @cd ..; $(MAKE) lint SUBDIRS=src -# CYGNUS LOCAL: Do not depend upon installdirs -install: +install: installdirs @for prog in $(PROGS); do \ echo Installing $$prog in $(bindir); \ $(INSTALL) $$prog $(bindir)/$$prog ; \ @@ -151,10 +144,10 @@ dist-dir: # Linking rules. -$(PROGS): ../lib/libcvs.a +$(PROGS): ../lib/libcvs.a ../zlib/libz.a cvs: $(OBJECTS) - $(CC) $(OBJECTS) ../lib/libcvs.a $(LIBS) $(LDFLAGS) -o $@ + $(CC) $(OBJECTS) ../lib/libcvs.a ../zlib/libz.a $(LIBS) $(LDFLAGS) -o $@ xlint: $(SOURCES) files= ; \ @@ -168,7 +161,7 @@ saber: $(SOURCES) # load $(CFLAGS) $(SOURCES) # load ../lib/libcvs.a $(LIBS) -cvsbug: cvsbug.sh +cvsbug: cvsbug.sh $(srcdir)/version.c echo > .fname \ cvs-`sed < $(srcdir)/version.c \ -e '/version_string/!d' \ @@ -184,6 +177,9 @@ cvsbug: cvsbug.sh $(OBJECTS): $(HEADERS) options.h +zlib.o: zlib.c + $(CC) $(CPPFLAGS) $(INCLUDES) -I$(top_srcdir)/zlib $(DEFS) $(CFLAGS) -c $(srcdir)/zlib.c + subdir = src Makefile: ../config.status Makefile.in cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status diff --git a/contrib/cvs/src/add.c b/contrib/cvs/src/add.c index 82efefe..b7d5e5d 100644 --- a/contrib/cvs/src/add.c +++ b/contrib/cvs/src/add.c @@ -27,7 +27,7 @@ #include "cvs.h" #include "savecwd.h" -static int add_directory PROTO((char *repository, char *dir)); +static int add_directory PROTO((char *repository, List *, char *dir)); static int build_entry PROTO((char *repository, char *user, char *options, char *message, List * entries, char *tag)); @@ -39,6 +39,47 @@ static const char *const add_usage[] = NULL }; +static char *combine_dir PROTO ((char *, char *)); + +/* Given a directory DIR and a subdirectory within it, SUBDIR, combine + the two into a new directory name. Returns a newly malloc'd string. + For now this is a fairly simple affair, but perhaps it will want + to have grander ambitions in the context of VMS or others (or perhaps + not, perhaps that should all be hidden inside CVS_FOPEN and libc and so + on, and CVS should just see foo/bar/baz style pathnames). */ +static char * +combine_dir (dir, subdir) + char *dir; + char *subdir; +{ + char *retval; + size_t dir_len; + + dir_len = strlen (dir); + retval = xmalloc (dir_len + strlen (subdir) + 10); + if (dir_len >= 2 + && dir[dir_len - 1] == '.' + && ISDIRSEP (dir[dir_len - 2])) + { + /* The dir name has an extraneous "." at the end. + I'm not completely sure that this is the best place + to strip it off--it is possible that Name_Repository + should do so, or it shouldn't be in the CVS/Repository + file in the first place. Fixing it here seems like + a safe, small change, but I'm not sure it catches + all the cases. */ + strncpy (retval, dir, dir_len - 2); + retval[dir_len - 2] = '\0'; + } + else + { + strcpy (retval, dir); + } + strcat (retval, "/"); + strcat (retval, subdir); + return retval; +} + int add (argc, argv) int argc; @@ -62,7 +103,7 @@ add (argc, argv) /* parse args */ optind = 1; - while ((c = getopt (argc, argv, "k:m:")) != -1) + while ((c = getopt (argc, argv, "+k:m:")) != -1) { switch (c) { @@ -92,42 +133,61 @@ add (argc, argv) #ifdef CLIENT_SUPPORT if (client_active) - { + { int i; start_server (); ign_setup (); if (options) send_arg(options); option_with_arg ("-m", message); for (i = 0; i < argc; ++i) - /* FIXME: Does this erroneously call Create_Admin in error - conditions which are only detected once the server gets its - hands on things? */ - if (isdir (argv[i])) + /* FIXME: Does this erroneously call Create_Admin in error + conditions which are only detected once the server gets its + hands on things? */ + if (isdir (argv[i])) { - char *tag; - char *date; - char *rcsdir = xmalloc (strlen (repository) - + strlen (argv[i]) + 10); + char *tag; + char *date; + int nonbranch; + char *rcsdir; - /* before we do anything else, see if we have any - per-directory tags */ - ParseTag (&tag, &date); + /* before we do anything else, see if we have any + per-directory tags */ + ParseTag (&tag, &date, &nonbranch); - sprintf (rcsdir, "%s/%s", repository, argv[i]); + rcsdir = combine_dir (repository, argv[i]); - Create_Admin (argv[i], argv[i], rcsdir, tag, date); + strip_trailing_slashes (argv[i]); - if (tag) - free (tag); - if (date) - free (date); - free (rcsdir); + Create_Admin (argv[i], argv[i], rcsdir, tag, date, nonbranch); + + if (tag) + free (tag); + if (date) + free (date); + free (rcsdir); + + if (strchr (argv[i], '/') == NULL) + Subdir_Register ((List *) NULL, (char *) NULL, argv[i]); + else + { + char *cp, *b; + + cp = xstrdup (argv[i]); + b = strrchr (cp, '/'); + *b++ = '\0'; + Subdir_Register ((List *) NULL, cp, b); + free (cp); + } } send_file_names (argc, argv, SEND_EXPAND_WILD); - send_files (argc, argv, 0, 0); + /* FIXME: should be able to pass SEND_NO_CONTENTS, I think. */ + send_files (argc, argv, 0, 0, SEND_BUILD_DIRS); send_to_server ("add\012", 0); + if (message) + free (message); + free (repository); return get_responses_and_close (); - } + } #endif entries = Entries_Open (0); @@ -136,7 +196,10 @@ add (argc, argv) for (i = 0; i < argc; i++) { int begin_err = err; +#ifdef SERVER_SUPPORT int begin_added_files = added_files; +#endif + struct file_info finfo; user = argv[i]; strip_trailing_slashes (user); @@ -148,8 +211,19 @@ add (argc, argv) continue; } - vers = Version_TS (repository, options, (char *) NULL, (char *) NULL, - user, 0, 0, entries, (RCSNode *) NULL); + memset (&finfo, 0, sizeof finfo); + finfo.file = user; + finfo.update_dir = ""; + finfo.fullname = user; + finfo.repository = repository; + finfo.entries = entries; + finfo.rcs = NULL; + + /* We pass force_tag_match as 1. If the directory has a + sticky branch tag, and there is already an RCS file which + does not have that tag, then the head revision is + meaningless to us. */ + vers = Version_TS (&finfo, options, NULL, NULL, 1, 0); if (vers->vn_user == NULL) { /* No entry available, ts_rcs is invalid */ @@ -168,7 +242,8 @@ add (argc, argv) * See if a directory exists in the repository with * the same name. If so, blow this request off. */ - char dname[PATH_MAX]; + char *dname = xmalloc (strlen (repository) + strlen (user) + + 10); (void) sprintf (dname, "%s/%s", repository, user); if (isdir (dname)) { @@ -179,29 +254,55 @@ add (argc, argv) dname); error (1, 0, "illegal filename overlap"); } + free (dname); - /* There is a user file, so build the entry for it */ - if (build_entry (repository, user, vers->options, - message, entries, vers->tag) != 0) - err++; - else + if (vers->options == NULL || *vers->options == '\0') { - added_files++; - if (!quiet) + /* No options specified on command line (or in + rcs file if it existed, e.g. the file exists + on another branch). Check for a value from + the wrapper stuff. */ + if (wrap_name_has (user, WRAP_RCSOPTION)) { - if (vers->tag) - error (0, 0, "\ + if (vers->options) + free (vers->options); + vers->options = wrap_rcsoption (user, 1); + } + } + + if (vers->nonbranch) + { + error (0, 0, + "cannot add file on non-branch tag %s", + vers->tag); + ++err; + } + else + { + /* There is a user file, so build the entry for it */ + if (build_entry (repository, user, vers->options, + message, entries, vers->tag) != 0) + err++; + else + { + added_files++; + if (!quiet) + { + if (vers->tag) + error (0, 0, "\ scheduling %s `%s' for addition on branch `%s'", - (wrap_name_has (user, WRAP_TOCVS) - ? "wrapper" - : "file"), - user, vers->tag); - else - error (0, 0, "scheduling %s `%s' for addition", - (wrap_name_has (user, WRAP_TOCVS) - ? "wrapper" - : "file"), - user); + (wrap_name_has (user, WRAP_TOCVS) + ? "wrapper" + : "file"), + user, vers->tag); + else + error (0, 0, + "scheduling %s `%s' for addition", + (wrap_name_has (user, WRAP_TOCVS) + ? "wrapper" + : "file"), + user); + } } } } @@ -210,20 +311,38 @@ scheduling %s `%s' for addition on branch `%s'", { if (isdir (user) && !wrap_name_has (user, WRAP_TOCVS)) { - error (0, 0, "the directory `%s' cannot be added because a file of the", user); - error (1, 0, "same name already exists in the repository."); + error (0, 0, "\ +the directory `%s' cannot be added because a file of the", user); + error (1, 0, "\ +same name already exists in the repository."); } else { - if (vers->tag) - error (0, 0, "file `%s' will be added on branch `%s' from version %s", - user, vers->tag, vers->vn_rcs); + if (vers->nonbranch) + { + error (0, 0, + "cannot add file on non-branch tag %s", + vers->tag); + ++err; + } else - error (0, 0, "version %s of `%s' will be resurrected", - vers->vn_rcs, user); - Register (entries, user, "0", vers->ts_user, NULL, - vers->tag, NULL, NULL); - ++added_files; + { + if (vers->tag) + error (0, 0, "\ +file `%s' will be added on branch `%s' from version %s", + user, vers->tag, vers->vn_rcs); + else + /* I'm not sure that mentioning + vers->vn_rcs makes any sense here; I + can't think of a way to word the + message which is not confusing. */ + error (0, 0, "\ +re-adding file %s (in place of dead revision %s)", + user, vers->vn_rcs); + Register (entries, user, "0", vers->ts_user, NULL, + vers->tag, NULL, NULL); + ++added_files; + } } } else @@ -259,8 +378,8 @@ scheduling %s `%s' for addition on branch `%s'", * There is no RCS file, so somebody else must've removed * it from under us */ - error (0, 0, - "cannot resurrect %s; RCS file removed by second party", user); + error (0, 0, "\ +cannot resurrect %s; RCS file removed by second party", user); err++; } else @@ -298,7 +417,8 @@ scheduling %s `%s' for addition on branch `%s'", else { /* The user file shouldn't be there */ - error (0, 0, "%s should be removed and is still there (or is back again)", user); + error (0, 0, "\ +%s should be removed and is still there (or is back again)", user); err++; } } @@ -316,7 +436,7 @@ scheduling %s `%s' for addition on branch `%s'", && isdir (user) && !wrap_name_has (user, WRAP_TOCVS)) { - err += add_directory (repository, user); + err += add_directory (repository, entries, user); continue; } #ifdef SERVER_SUPPORT @@ -332,6 +452,7 @@ scheduling %s `%s' for addition on branch `%s'", if (message) free (message); + free (repository); return (err); } @@ -344,14 +465,16 @@ scheduling %s `%s' for addition on branch `%s'", * Returns 1 on failure, 0 on success. */ static int -add_directory (repository, dir) +add_directory (repository, entries, dir) char *repository; + List *entries; char *dir; { - char rcsdir[PATH_MAX]; + char *rcsdir = NULL; struct saved_cwd cwd; - char message[PATH_MAX + 100]; + char *message = NULL; char *tag, *date; + int nonbranch; if (strchr (dir, '/') != NULL) { @@ -366,12 +489,12 @@ add_directory (repository, dir) } /* before we do anything else, see if we have any per-directory tags */ - ParseTag (&tag, &date); + ParseTag (&tag, &date, &nonbranch); /* now, remember where we were, so we can get back */ if (save_cwd (&cwd)) return (1); - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) { error (0, errno, "cannot chdir to %s", dir); return (1); @@ -386,7 +509,7 @@ add_directory (repository, dir) goto out; } - (void) sprintf (rcsdir, "%s/%s", repository, dir); + rcsdir = combine_dir (repository, dir); if (isfile (rcsdir) && !isdir (rcsdir)) { error (0, 0, "%s is not a directory; %s not added", rcsdir, dir); @@ -394,6 +517,7 @@ add_directory (repository, dir) } /* setup the log message */ + message = xmalloc (strlen (rcsdir) + 80); (void) sprintf (message, "Directory %s added to the repository\n", rcsdir); if (tag) { @@ -413,30 +537,27 @@ add_directory (repository, dir) mode_t omask; Node *p; List *ulist; + struct logfile_info *li; -#if 0 - char line[MAXLINELEN]; - - (void) printf ("Add directory %s to the repository (y/n) [n] ? ", - rcsdir); - (void) fflush (stdout); - clearerr (stdin); - if (fgets (line, sizeof (line), stdin) == NULL || - (line[0] != 'y' && line[0] != 'Y')) - { - error (0, 0, "directory %s not added", rcsdir); - goto out; - } -#endif + /* There used to be some code here which would prompt for + whether to add the directory. The details of that code had + bitrotted, but more to the point it can't work + client/server, doesn't ask in the right way for GUIs, etc. + A better way of making it harder to accidentally add + directories would be to have to add and commit directories + like for files. The code was #if 0'd at least since CVS 1.5. */ - omask = umask (cvsumask); - if (CVS_MKDIR (rcsdir, 0777) < 0) + if (!noexec) { - error (0, errno, "cannot mkdir %s", rcsdir); + omask = umask (cvsumask); + if (CVS_MKDIR (rcsdir, 0777) < 0) + { + error (0, errno, "cannot mkdir %s", rcsdir); + (void) umask (omask); + goto out; + } (void) umask (omask); - goto out; } - (void) umask (omask); /* * Set up an update list with a single title node for Update_Logfile @@ -446,28 +567,45 @@ add_directory (repository, dir) p->type = UPDATE; p->delproc = update_delproc; p->key = xstrdup ("- New directory"); - p->data = (char *) T_TITLE; + li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); + li->type = T_TITLE; + li->tag = xstrdup (tag); + li->rev_old = li->rev_new = NULL; + p->data = (char *) li; (void) addnode (ulist, p); - Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist); + Update_Logfile (rcsdir, message, (FILE *) NULL, ulist); dellist (&ulist); } #ifdef SERVER_SUPPORT if (!server_active) - Create_Admin (".", dir, rcsdir, tag, date); + Create_Admin (".", dir, rcsdir, tag, date, nonbranch); #else - Create_Admin (".", dir, rcsdir, tag, date); + Create_Admin (".", dir, rcsdir, tag, date, nonbranch); #endif if (tag) free (tag); if (date) free (date); + if (restore_cwd (&cwd, NULL)) + error_exit (); + free_cwd (&cwd); + + Subdir_Register (entries, (char *) NULL, dir); + (void) printf ("%s", message); + free (rcsdir); + free (message); + + return (0); + out: if (restore_cwd (&cwd, NULL)) - exit (EXIT_FAILURE); + error_exit (); free_cwd (&cwd); + if (rcsdir != NULL) + free (rcsdir); return (0); } @@ -484,8 +622,8 @@ build_entry (repository, user, options, message, entries, tag) List *entries; char *tag; { - char fname[PATH_MAX]; - char line[MAXLINELEN]; + char *fname; + char *line; FILE *fp; if (noexec) @@ -496,19 +634,23 @@ build_entry (repository, user, options, message, entries, tag) * file user,t. If the "message" argument is set, use it as the * initial creation log (which typically describes the file). */ + fname = xmalloc (strlen (user) + 80); (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG); fp = open_file (fname, "w+"); if (message && fputs (message, fp) == EOF) error (1, errno, "cannot write to %s", fname); if (fclose(fp) == EOF) error(1, errno, "cannot close %s", fname); + free (fname); /* * Create the entry now, since this allows the user to interrupt us above * without needing to clean anything up (well, we could clean up the * ,t file, but who cares). */ + line = xmalloc (strlen (user) + 20); (void) sprintf (line, "Initial %s", user); Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0); + free (line); return (0); } diff --git a/contrib/cvs/src/admin.c b/contrib/cvs/src/admin.c index 214318a..d1401f4 100644 --- a/contrib/cvs/src/admin.c +++ b/contrib/cvs/src/admin.c @@ -16,8 +16,10 @@ #include <grp.h> #endif -static Dtype admin_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int admin_fileproc PROTO((struct file_info *finfo)); +static Dtype admin_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int admin_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static const char *const admin_usage[] = { @@ -36,6 +38,7 @@ admin (argc, argv) int err; #ifdef CVS_ADMIN_GROUP struct group *grp; + struct group *getgrnam(); #endif if (argc <= 1) usage (admin_usage); @@ -75,8 +78,6 @@ admin (argc, argv) av = argv + 1; argv += ac; ac--; - if (ac == 0 || argc == 0) - usage (admin_usage); #ifdef CLIENT_SUPPORT if (client_active) @@ -88,14 +89,11 @@ admin (argc, argv) ign_setup (); - for (i = 0; i <= ac; ++i) /* XXX send -ko too with i = 0 */ + for (i = 0; i < ac; ++i) /* XXX send -ko too with i = 0 */ send_arg (av[i]); send_file_names (argc, argv, SEND_EXPAND_WILD); - /* FIXME: We shouldn't have to send current files, but I'm not sure - whether it works. So send the files -- - it's slower but it works. */ - send_files (argc, argv, 0, 0); + send_files (argc, argv, 0, 0, SEND_NO_CONTENTS); send_to_server ("admin\012", 0); return get_responses_and_close (); } @@ -103,8 +101,8 @@ admin (argc, argv) /* start the recursion processor */ err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc, - (DIRLEAVEPROC) NULL, argc, argv, 0, - W_LOCAL, 0, 1, (char *) NULL, 1, 0); + (DIRLEAVEPROC) NULL, NULL, argc, argv, 0, + W_LOCAL, 0, 1, (char *) NULL, 1); return (err); } @@ -113,7 +111,8 @@ admin (argc, argv) */ /* ARGSUSED */ static int -admin_fileproc (finfo) +admin_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { Vers_TS *vers; @@ -123,8 +122,7 @@ admin_fileproc (finfo) int retcode = 0; int status = 0; - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL, - finfo->file, 0, 0, finfo->entries, finfo->rcs); + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); version = vers->vn_user; if (version == NULL) @@ -157,10 +155,12 @@ admin_fileproc (finfo) */ /* ARGSUSED */ static Dtype -admin_dirproc (dir, repos, update_dir) +admin_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "Administrating %s", update_dir); diff --git a/contrib/cvs/src/buffer.c b/contrib/cvs/src/buffer.c new file mode 100644 index 0000000..d6ea895 --- /dev/null +++ b/contrib/cvs/src/buffer.c @@ -0,0 +1,1294 @@ +/* Code for the buffer data structure. */ + +#include <assert.h> +#include "cvs.h" +#include "buffer.h" + +#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) + +/* OS/2 doesn't have EIO. FIXME: this whole notion of turning + a different error into EIO strikes me as pretty dubious. */ +#if !defined (EIO) +#define EIO EBADPOS +#endif + +/* Linked list of available buffer_data structures. */ +static struct buffer_data *free_buffer_data; + +/* Local functions. */ +static void allocate_buffer_datas PROTO((void)); +static struct buffer_data *get_buffer_data PROTO((void)); + +/* Initialize a buffer structure. */ + +struct buffer * +buf_initialize (input, output, flush, block, shutdown, memory, closure) + int (*input) PROTO((void *, char *, int, int, int *)); + int (*output) PROTO((void *, const char *, int, int *)); + int (*flush) PROTO((void *)); + int (*block) PROTO((void *, int)); + int (*shutdown) PROTO((void *)); + void (*memory) PROTO((struct buffer *)); + void *closure; +{ + struct buffer *buf; + + buf = (struct buffer *) xmalloc (sizeof (struct buffer)); + buf->data = NULL; + buf->last = NULL; + buf->nonblocking = 0; + buf->input = input; + buf->output = output; + buf->flush = flush; + buf->block = block; + buf->shutdown = shutdown; + buf->memory_error = memory; + buf->closure = closure; + return buf; +} + +/* Initialize a buffer structure which is not to be used for I/O. */ + +struct buffer * +buf_nonio_initialize (memory) + void (*memory) PROTO((struct buffer *)); +{ + return (buf_initialize + ((int (*) PROTO((void *, char *, int, int, int *))) NULL, + (int (*) PROTO((void *, const char *, int, int *))) NULL, + (int (*) PROTO((void *))) NULL, + (int (*) PROTO((void *, int))) NULL, + (int (*) PROTO((void *))) NULL, + memory, + (void *) NULL)); +} + +/* Allocate more buffer_data structures. */ + +static void +allocate_buffer_datas () +{ + struct buffer_data *alc; + char *space; + int i; + + /* Allocate buffer_data structures in blocks of 16. */ +#define ALLOC_COUNT (16) + + alc = ((struct buffer_data *) + malloc (ALLOC_COUNT * sizeof (struct buffer_data))); + space = (char *) valloc (ALLOC_COUNT * BUFFER_DATA_SIZE); + if (alc == NULL || space == NULL) + return; + for (i = 0; i < ALLOC_COUNT; i++, alc++, space += BUFFER_DATA_SIZE) + { + alc->next = free_buffer_data; + free_buffer_data = alc; + alc->text = space; + } +} + +/* Get a new buffer_data structure. */ + +static struct buffer_data * +get_buffer_data () +{ + struct buffer_data *ret; + + if (free_buffer_data == NULL) + { + allocate_buffer_datas (); + if (free_buffer_data == NULL) + return NULL; + } + + ret = free_buffer_data; + free_buffer_data = ret->next; + return ret; +} + +/* See whether a buffer is empty. */ + +int +buf_empty_p (buf) + struct buffer *buf; +{ + struct buffer_data *data; + + for (data = buf->data; data != NULL; data = data->next) + if (data->size > 0) + return 0; + return 1; +} + +#ifdef SERVER_FLOWCONTROL +/* + * Count how much data is stored in the buffer.. + * Note that each buffer is a malloc'ed chunk BUFFER_DATA_SIZE. + */ + +int +buf_count_mem (buf) + struct buffer *buf; +{ + struct buffer_data *data; + int mem = 0; + + for (data = buf->data; data != NULL; data = data->next) + mem += BUFFER_DATA_SIZE; + + return mem; +} +#endif /* SERVER_FLOWCONTROL */ + +/* Add data DATA of length LEN to BUF. */ + +void +buf_output (buf, data, len) + struct buffer *buf; + const char *data; + int len; +{ + if (buf->data != NULL + && (((buf->last->text + BUFFER_DATA_SIZE) + - (buf->last->bufp + buf->last->size)) + >= len)) + { + memcpy (buf->last->bufp + buf->last->size, data, len); + buf->last->size += len; + return; + } + + while (1) + { + struct buffer_data *newdata; + + newdata = get_buffer_data (); + if (newdata == NULL) + { + (*buf->memory_error) (buf); + return; + } + + if (buf->data == NULL) + buf->data = newdata; + else + buf->last->next = newdata; + newdata->next = NULL; + buf->last = newdata; + + newdata->bufp = newdata->text; + + if (len <= BUFFER_DATA_SIZE) + { + newdata->size = len; + memcpy (newdata->text, data, len); + return; + } + + newdata->size = BUFFER_DATA_SIZE; + memcpy (newdata->text, data, BUFFER_DATA_SIZE); + + data += BUFFER_DATA_SIZE; + len -= BUFFER_DATA_SIZE; + } + + /*NOTREACHED*/ +} + +/* Add a '\0' terminated string to BUF. */ + +void +buf_output0 (buf, string) + struct buffer *buf; + const char *string; +{ + buf_output (buf, string, strlen (string)); +} + +/* Add a single character to BUF. */ + +void +buf_append_char (buf, ch) + struct buffer *buf; + int ch; +{ + if (buf->data != NULL + && (buf->last->text + BUFFER_DATA_SIZE + != buf->last->bufp + buf->last->size)) + { + *(buf->last->bufp + buf->last->size) = ch; + ++buf->last->size; + } + else + { + char b; + + b = ch; + buf_output (buf, &b, 1); + } +} + +/* + * Send all the output we've been saving up. Returns 0 for success or + * errno code. If the buffer has been set to be nonblocking, this + * will just write until the write would block. + */ + +int +buf_send_output (buf) + struct buffer *buf; +{ + if (buf->output == NULL) + abort (); + + while (buf->data != NULL) + { + struct buffer_data *data; + + data = buf->data; + + if (data->size > 0) + { + int status, nbytes; + + status = (*buf->output) (buf->closure, data->bufp, data->size, + &nbytes); + if (status != 0) + { + /* Some sort of error. Discard the data, and return. */ + + buf->last->next = free_buffer_data; + free_buffer_data = buf->data; + buf->data = NULL; + buf->last = NULL; + + return status; + } + + if (nbytes != data->size) + { + /* Not all the data was written out. This is only + permitted in nonblocking mode. Adjust the buffer, + and return. */ + + assert (buf->nonblocking); + + data->size -= nbytes; + data->bufp += nbytes; + + return 0; + } + } + + buf->data = data->next; + data->next = free_buffer_data; + free_buffer_data = data; + } + + buf->last = NULL; + + return 0; +} + +/* + * Flush any data queued up in the buffer. If BLOCK is nonzero, then + * if the buffer is in nonblocking mode, put it into blocking mode for + * the duration of the flush. This returns 0 on success, or an error + * code. + */ + +int +buf_flush (buf, block) + struct buffer *buf; + int block; +{ + int nonblocking; + int status; + + if (buf->flush == NULL) + abort (); + + nonblocking = buf->nonblocking; + if (nonblocking && block) + { + status = set_block (buf); + if (status != 0) + return status; + } + + status = buf_send_output (buf); + if (status == 0) + status = (*buf->flush) (buf->closure); + + if (nonblocking && block) + { + int blockstat; + + blockstat = set_nonblock (buf); + if (status == 0) + status = blockstat; + } + + return status; +} + +/* + * Set buffer BUF to nonblocking I/O. Returns 0 for success or errno + * code. + */ + +int +set_nonblock (buf) + struct buffer *buf; +{ + int status; + + if (buf->nonblocking) + return 0; + if (buf->block == NULL) + abort (); + status = (*buf->block) (buf->closure, 0); + if (status != 0) + return status; + buf->nonblocking = 1; + return 0; +} + +/* + * Set buffer BUF to blocking I/O. Returns 0 for success or errno + * code. + */ + +int +set_block (buf) + struct buffer *buf; +{ + int status; + + if (! buf->nonblocking) + return 0; + if (buf->block == NULL) + abort (); + status = (*buf->block) (buf->closure, 1); + if (status != 0) + return status; + buf->nonblocking = 0; + return 0; +} + +/* + * Send a character count and some output. Returns errno code or 0 for + * success. + * + * Sending the count in binary is OK since this is only used on a pipe + * within the same system. + */ + +int +buf_send_counted (buf) + struct buffer *buf; +{ + int size; + struct buffer_data *data; + + size = 0; + for (data = buf->data; data != NULL; data = data->next) + size += data->size; + + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return ENOMEM; + } + + data->next = buf->data; + buf->data = data; + if (buf->last == NULL) + buf->last = data; + + data->bufp = data->text; + data->size = sizeof (int); + + *((int *) data->text) = size; + + return buf_send_output (buf); +} + +/* + * Send a special count. COUNT should be negative. It will be + * handled speciallyi by buf_copy_counted. This function returns 0 or + * an errno code. + * + * Sending the count in binary is OK since this is only used on a pipe + * within the same system. + */ + +int +buf_send_special_count (buf, count) + struct buffer *buf; + int count; +{ + struct buffer_data *data; + + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return ENOMEM; + } + + data->next = buf->data; + buf->data = data; + if (buf->last == NULL) + buf->last = data; + + data->bufp = data->text; + data->size = sizeof (int); + + *((int *) data->text) = count; + + return buf_send_output (buf); +} + +/* Append a list of buffer_data structures to an buffer. */ + +void +buf_append_data (buf, data, last) + struct buffer *buf; + struct buffer_data *data; + struct buffer_data *last; +{ + if (data != NULL) + { + if (buf->data == NULL) + buf->data = data; + else + buf->last->next = data; + buf->last = last; + } +} + +/* + * Copy the contents of file F into buffer_data structures. We can't + * copy directly into an buffer, because we want to handle failure and + * succeess differently. Returns 0 on success, or -2 if out of + * memory, or a status code on error. Since the caller happens to + * know the size of the file, it is passed in as SIZE. On success, + * this function sets *RETP and *LASTP, which may be passed to + * buf_append_data. + */ + +int +buf_read_file (f, size, retp, lastp) + FILE *f; + long size; + struct buffer_data **retp; + struct buffer_data **lastp; +{ + int status; + + *retp = NULL; + *lastp = NULL; + + while (size > 0) + { + struct buffer_data *data; + int get; + + data = get_buffer_data (); + if (data == NULL) + { + status = -2; + goto error_return; + } + + if (*retp == NULL) + *retp = data; + else + (*lastp)->next = data; + data->next = NULL; + *lastp = data; + + data->bufp = data->text; + data->size = 0; + + if (size > BUFFER_DATA_SIZE) + get = BUFFER_DATA_SIZE; + else + get = size; + + errno = EIO; + if (fread (data->text, get, 1, f) != 1) + { + status = errno; + goto error_return; + } + + data->size += get; + size -= get; + } + + return 0; + + error_return: + if (*retp != NULL) + { + (*lastp)->next = free_buffer_data; + free_buffer_data = *retp; + } + return status; +} + +/* + * Copy the contents of file F into buffer_data structures. We can't + * copy directly into an buffer, because we want to handle failure and + * succeess differently. Returns 0 on success, or -2 if out of + * memory, or a status code on error. On success, this function sets + * *RETP and *LASTP, which may be passed to buf_append_data. + */ + +int +buf_read_file_to_eof (f, retp, lastp) + FILE *f; + struct buffer_data **retp; + struct buffer_data **lastp; +{ + int status; + + *retp = NULL; + *lastp = NULL; + + while (!feof (f)) + { + struct buffer_data *data; + int get, nread; + + data = get_buffer_data (); + if (data == NULL) + { + status = -2; + goto error_return; + } + + if (*retp == NULL) + *retp = data; + else + (*lastp)->next = data; + data->next = NULL; + *lastp = data; + + data->bufp = data->text; + data->size = 0; + + get = BUFFER_DATA_SIZE; + + errno = EIO; + nread = fread (data->text, 1, get, f); + if (nread == 0 && !feof (f)) + { + status = errno; + goto error_return; + } + + data->size = nread; + } + + return 0; + + error_return: + if (*retp != NULL) + { + (*lastp)->next = free_buffer_data; + free_buffer_data = *retp; + } + return status; +} + +/* Return the number of bytes in a chain of buffer_data structures. */ + +int +buf_chain_length (buf) + struct buffer_data *buf; +{ + int size = 0; + while (buf) + { + size += buf->size; + buf = buf->next; + } + return size; +} + +/* + * Read an arbitrary amount of data into an input buffer. The buffer + * will be in nonblocking mode, and we just grab what we can. Return + * 0 on success, or -1 on end of file, or -2 if out of memory, or an + * error code. If COUNTP is not NULL, *COUNTP is set to the number of + * bytes read. + */ + +int +buf_input_data (buf, countp) + struct buffer *buf; + int *countp; +{ + if (buf->input == NULL) + abort (); + + if (countp != NULL) + *countp = 0; + + while (1) + { + int get; + int status, nbytes; + + if (buf->data == NULL + || (buf->last->bufp + buf->last->size + == buf->last->text + BUFFER_DATA_SIZE)) + { + struct buffer_data *data; + + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return -2; + } + + if (buf->data == NULL) + buf->data = data; + else + buf->last->next = data; + data->next = NULL; + buf->last = data; + + data->bufp = data->text; + data->size = 0; + } + + get = ((buf->last->text + BUFFER_DATA_SIZE) + - (buf->last->bufp + buf->last->size)); + + status = (*buf->input) (buf->closure, + buf->last->bufp + buf->last->size, + 0, get, &nbytes); + if (status != 0) + return status; + + buf->last->size += nbytes; + if (countp != NULL) + *countp += nbytes; + + if (nbytes < get) + { + /* If we did not fill the buffer, then presumably we read + all the available data. */ + return 0; + } + } + + /*NOTREACHED*/ +} + +/* + * Read a line (characters up to a \012) from an input buffer. (We + * use \012 rather than \n for the benefit of non Unix clients for + * which \n means something else). This returns 0 on success, or -1 + * on end of file, or -2 if out of memory, or an error code. If it + * succeeds, it sets *LINE to an allocated buffer holding the contents + * of the line. The trailing \012 is not included in the buffer. If + * LENP is not NULL, then *LENP is set to the number of bytes read; + * strlen may not work, because there may be embedded null bytes. + */ + +int +buf_read_line (buf, line, lenp) + struct buffer *buf; + char **line; + int *lenp; +{ + if (buf->input == NULL) + abort (); + + *line = NULL; + + while (1) + { + int len, finallen = 0; + struct buffer_data *data; + char *nl; + + /* See if there is a newline in BUF. */ + len = 0; + for (data = buf->data; data != NULL; data = data->next) + { + nl = memchr (data->bufp, '\012', data->size); + if (nl != NULL) + { + finallen = nl - data->bufp; + len += finallen; + break; + } + len += data->size; + } + + /* If we found a newline, copy the line into a memory buffer, + and remove it from BUF. */ + if (data != NULL) + { + char *p; + struct buffer_data *nldata; + + p = malloc (len + 1); + if (p == NULL) + return -2; + *line = p; + + nldata = data; + data = buf->data; + while (data != nldata) + { + struct buffer_data *next; + + memcpy (p, data->bufp, data->size); + p += data->size; + next = data->next; + data->next = free_buffer_data; + free_buffer_data = data; + data = next; + } + + memcpy (p, data->bufp, finallen); + p[finallen] = '\0'; + + data->size -= finallen + 1; + data->bufp = nl + 1; + buf->data = data; + + if (lenp != NULL) + *lenp = len; + + return 0; + } + + /* Read more data until we get a newline. */ + while (1) + { + int size, status, nbytes; + char *mem; + + if (buf->data == NULL + || (buf->last->bufp + buf->last->size + == buf->last->text + BUFFER_DATA_SIZE)) + { + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return -2; + } + + if (buf->data == NULL) + buf->data = data; + else + buf->last->next = data; + data->next = NULL; + buf->last = data; + + data->bufp = data->text; + data->size = 0; + } + + mem = buf->last->bufp + buf->last->size; + size = (buf->last->text + BUFFER_DATA_SIZE) - mem; + + /* We need to read at least 1 byte. We can handle up to + SIZE bytes. This will only be efficient if the + underlying communication stream does its own buffering, + or is clever about getting more than 1 byte at a time. */ + status = (*buf->input) (buf->closure, mem, 1, size, &nbytes); + if (status != 0) + return status; + + buf->last->size += nbytes; + + /* Optimize slightly to avoid an unnecessary call to + memchr. */ + if (nbytes == 1) + { + if (*mem == '\012') + break; + } + else + { + if (memchr (mem, '\012', nbytes) != NULL) + break; + } + } + } +} + +/* + * Extract data from the input buffer BUF. This will read up to WANT + * bytes from the buffer. It will set *RETDATA to point at the bytes, + * and set *GOT to the number of bytes to be found there. Any buffer + * call which uses BUF may change the contents of the buffer at *DATA, + * so the data should be fully processed before any further calls are + * made. This returns 0 on success, or -1 on end of file, or -2 if + * out of memory, or an error code. + */ + +int +buf_read_data (buf, want, retdata, got) + struct buffer *buf; + int want; + char **retdata; + int *got; +{ + if (buf->input == NULL) + abort (); + + while (buf->data != NULL && buf->data->size == 0) + { + struct buffer_data *next; + + next = buf->data->next; + buf->data->next = free_buffer_data; + free_buffer_data = buf->data; + buf->data = next; + if (next == NULL) + buf->last = NULL; + } + + if (buf->data == NULL) + { + struct buffer_data *data; + int get, status, nbytes; + + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return -2; + } + + buf->data = data; + buf->last = data; + data->next = NULL; + data->bufp = data->text; + data->size = 0; + + if (want < BUFFER_DATA_SIZE) + get = want; + else + get = BUFFER_DATA_SIZE; + status = (*buf->input) (buf->closure, data->bufp, get, + BUFFER_DATA_SIZE, &nbytes); + if (status != 0) + return status; + + data->size = nbytes; + } + + *retdata = buf->data->bufp; + if (want < buf->data->size) + { + *got = want; + buf->data->size -= want; + buf->data->bufp += want; + } + else + { + *got = buf->data->size; + buf->data->size = 0; + } + + return 0; +} + +/* + * Copy lines from an input buffer to an output buffer. This copies + * all complete lines (characters up to a newline) from INBUF to + * OUTBUF. Each line in OUTBUF is preceded by the character COMMAND + * and a space. + */ + +void +buf_copy_lines (outbuf, inbuf, command) + struct buffer *outbuf; + struct buffer *inbuf; + int command; +{ + while (1) + { + struct buffer_data *data; + struct buffer_data *nldata; + char *nl; + int len; + + /* See if there is a newline in INBUF. */ + nldata = NULL; + nl = NULL; + for (data = inbuf->data; data != NULL; data = data->next) + { + nl = memchr (data->bufp, '\n', data->size); + if (nl != NULL) + { + nldata = data; + break; + } + } + + if (nldata == NULL) + { + /* There are no more lines in INBUF. */ + return; + } + + /* Put in the command. */ + buf_append_char (outbuf, command); + buf_append_char (outbuf, ' '); + + if (inbuf->data != nldata) + { + /* + * Simply move over all the buffers up to the one containing + * the newline. + */ + for (data = inbuf->data; data->next != nldata; data = data->next) + ; + data->next = NULL; + buf_append_data (outbuf, inbuf->data, data); + inbuf->data = nldata; + } + + /* + * If the newline is at the very end of the buffer, just move + * the buffer onto OUTBUF. Otherwise we must copy the data. + */ + len = nl + 1 - nldata->bufp; + if (len == nldata->size) + { + inbuf->data = nldata->next; + if (inbuf->data == NULL) + inbuf->last = NULL; + + nldata->next = NULL; + buf_append_data (outbuf, nldata, nldata); + } + else + { + buf_output (outbuf, nldata->bufp, len); + nldata->bufp += len; + nldata->size -= len; + } + } +} + +/* + * Copy counted data from one buffer to another. The count is an + * integer, host size, host byte order (it is only used across a + * pipe). If there is enough data, it should be moved over. If there + * is not enough data, it should remain on the original buffer. A + * negative count is a special case. if one is seen, *SPECIAL is set + * to the (negative) count value and no additional data is gathered + * from the buffer; normally *SPECIAL is set to 0. This function + * returns the number of bytes it needs to see in order to actually + * copy something over. + */ + +int +buf_copy_counted (outbuf, inbuf, special) + struct buffer *outbuf; + struct buffer *inbuf; + int *special; +{ + *special = 0; + + while (1) + { + struct buffer_data *data; + int need; + union + { + char intbuf[sizeof (int)]; + int i; + } u; + char *intp; + int count; + struct buffer_data *start; + int startoff; + struct buffer_data *stop; + int stopwant; + + /* See if we have enough bytes to figure out the count. */ + need = sizeof (int); + intp = u.intbuf; + for (data = inbuf->data; data != NULL; data = data->next) + { + if (data->size >= need) + { + memcpy (intp, data->bufp, need); + break; + } + memcpy (intp, data->bufp, data->size); + intp += data->size; + need -= data->size; + } + if (data == NULL) + { + /* We don't have enough bytes to form an integer. */ + return need; + } + + count = u.i; + start = data; + startoff = need; + + if (count < 0) + { + /* A negative COUNT is a special case meaning that we + don't need any further information. */ + stop = start; + stopwant = 0; + } + else + { + /* + * We have an integer in COUNT. We have gotten all the + * data from INBUF in all buffers before START, and we + * have gotten STARTOFF bytes from START. See if we have + * enough bytes remaining in INBUF. + */ + need = count - (start->size - startoff); + if (need <= 0) + { + stop = start; + stopwant = count; + } + else + { + for (data = start->next; data != NULL; data = data->next) + { + if (need <= data->size) + break; + need -= data->size; + } + if (data == NULL) + { + /* We don't have enough bytes. */ + return need; + } + stop = data; + stopwant = need; + } + } + + /* + * We have enough bytes. Free any buffers in INBUF before + * START, and remove STARTOFF bytes from START, so that we can + * forget about STARTOFF. + */ + start->bufp += startoff; + start->size -= startoff; + + if (start->size == 0) + start = start->next; + + if (stop->size == stopwant) + { + stop = stop->next; + stopwant = 0; + } + + while (inbuf->data != start) + { + data = inbuf->data; + inbuf->data = data->next; + data->next = free_buffer_data; + free_buffer_data = data; + } + + /* If COUNT is negative, set *SPECIAL and get out now. */ + if (count < 0) + { + *special = count; + return 0; + } + + /* + * We want to copy over the bytes from START through STOP. We + * only want STOPWANT bytes from STOP. + */ + + if (start != stop) + { + /* Attach the buffers from START through STOP to OUTBUF. */ + for (data = start; data->next != stop; data = data->next) + ; + inbuf->data = stop; + data->next = NULL; + buf_append_data (outbuf, start, data); + } + + if (stopwant > 0) + { + buf_output (outbuf, stop->bufp, stopwant); + stop->bufp += stopwant; + stop->size -= stopwant; + } + } + + /*NOTREACHED*/ +} + +/* Shut down a buffer. This returns 0 on success, or an errno code. */ + +int +buf_shutdown (buf) + struct buffer *buf; +{ + if (buf->shutdown) + return (*buf->shutdown) (buf->closure); + return 0; +} + +/* The simplest type of buffer is one built on top of a stdio FILE. + For simplicity, and because it is all that is required, we do not + implement setting this type of buffer into nonblocking mode. The + closure field is just a FILE *. */ + +static int stdio_buffer_input PROTO((void *, char *, int, int, int *)); +static int stdio_buffer_output PROTO((void *, const char *, int, int *)); +static int stdio_buffer_flush PROTO((void *)); + +/* Initialize a buffer built on a stdio FILE. */ + +struct buffer +*stdio_buffer_initialize (fp, input, memory) + FILE *fp; + int input; + void (*memory) PROTO((struct buffer *)); +{ + return buf_initialize (input ? stdio_buffer_input : NULL, + input ? NULL : stdio_buffer_output, + input ? NULL : stdio_buffer_flush, + (int (*) PROTO((void *, int))) NULL, + (int (*) PROTO((void *))) NULL, + memory, + (void *) fp); +} + +/* The buffer input function for a buffer built on a stdio FILE. */ + +static int +stdio_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + FILE *fp = (FILE *) closure; + int nbytes; + + /* Since stdio does its own buffering, we don't worry about + getting more bytes than we need. */ + + if (need == 0 || need == 1) + { + int ch; + + ch = getc (fp); + + if (ch == EOF) + { + if (feof (fp)) + return -1; + else if (errno == 0) + return EIO; + else + return errno; + } + + *data = ch; + *got = 1; + return 0; + } + + nbytes = fread (data, 1, need, fp); + + if (nbytes == 0) + { + *got = 0; + if (feof (fp)) + return -1; + else if (errno == 0) + return EIO; + else + return errno; + } + + *got = nbytes; + + return 0; +} + +/* The buffer output function for a buffer built on a stdio FILE. */ + +static int +stdio_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + FILE *fp = (FILE *) closure; + + *wrote = 0; + + while (have > 0) + { + int nbytes; + + nbytes = fwrite (data, 1, have, fp); + + if (nbytes != have) + { + if (errno == 0) + return EIO; + else + return errno; + } + + *wrote += nbytes; + have -= nbytes; + data += nbytes; + } + + return 0; +} + +/* The buffer flush function for a buffer built on a stdio FILE. */ + +static int +stdio_buffer_flush (closure) + void *closure; +{ + FILE *fp = (FILE *) closure; + + if (fflush (fp) != 0) + { + if (errno == 0) + return EIO; + else + return errno; + } + + return 0; +} + +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ diff --git a/contrib/cvs/src/buffer.h b/contrib/cvs/src/buffer.h new file mode 100644 index 0000000..b62456c --- /dev/null +++ b/contrib/cvs/src/buffer.h @@ -0,0 +1,142 @@ +/* Declarations concerning the buffer data structure. */ + +#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) + +/* + * We must read data from a child process and send it across the + * network. We do not want to block on writing to the network, so we + * store the data from the child process in memory. A BUFFER + * structure holds the status of one communication, and uses a linked + * list of buffer_data structures to hold data. + */ + +struct buffer +{ + /* Data. */ + struct buffer_data *data; + + /* Last buffer on data chain. */ + struct buffer_data *last; + + /* Nonzero if the buffer is in nonblocking mode. */ + int nonblocking; + + /* Functions must be provided to transfer data in and out of the + buffer. Either the input or output field must be set, but not + both. */ + + /* Read data into the buffer DATA. There is room for up to SIZE + bytes. In blocking mode, wait until some input, at least NEED + bytes, is available (NEED may be 0 but that is the same as NEED + == 1). In non-blocking mode return immediately no matter how + much input is available; NEED is ignored. Return 0 on success, + or -1 on end of file, or an errno code. Set the number of + bytes read in *GOT. + + If there are a nonzero number of bytes available, less than NEED, + followed by end of file, just read those bytes and return 0. */ + int (*input) PROTO((void *closure, char *data, int need, int size, + int *got)); + + /* Write data. This should write up to HAVE bytes from DATA. + This should return 0 on success, or an errno code. It should + set the number of bytes written in *WROTE. */ + int (*output) PROTO((void *closure, const char *data, int have, + int *wrote)); + + /* Flush any data which may be buffered up after previous calls to + OUTPUT. This should return 0 on success, or an errno code. */ + int (*flush) PROTO((void *closure)); + + /* Change the blocking mode of the underlying communication + stream. If BLOCK is non-zero, it should be placed into + blocking mode. Otherwise, it should be placed into + non-blocking mode. This should return 0 on success, or an + errno code. */ + int (*block) PROTO ((void *closure, int block)); + + /* Shut down the communication stream. This does not mean that it + should be closed. It merely means that no more data will be + read or written, and that any final processing that is + appropriate should be done at this point. This may be NULL. + It should return 0 on success, or an errno code. This entry + point exists for the compression code. */ + int (*shutdown) PROTO((void *closure)); + + /* This field is passed to the INPUT, OUTPUT, and BLOCK functions. */ + void *closure; + + /* Function to call if we can't allocate memory. */ + void (*memory_error) PROTO((struct buffer *)); +}; + +/* Data is stored in lists of these structures. */ + +struct buffer_data +{ + /* Next buffer in linked list. */ + struct buffer_data *next; + + /* + * A pointer into the data area pointed to by the text field. This + * is where to find data that has not yet been written out. + */ + char *bufp; + + /* The number of data bytes found at BUFP. */ + int size; + + /* + * Actual buffer. This never changes after the structure is + * allocated. The buffer is BUFFER_DATA_SIZE bytes. + */ + char *text; +}; + +/* The size we allocate for each buffer_data structure. */ +#define BUFFER_DATA_SIZE (4096) + +extern struct buffer *buf_initialize PROTO((int (*) (void *, char *, int, + int, int *), + int (*) (void *, const char *, + int, int *), + int (*) (void *), + int (*) (void *, int), + int (*) (void *), + void (*) (struct buffer *), + void *)); +extern struct buffer *buf_nonio_initialize PROTO((void (*) (struct buffer *))); +extern struct buffer *stdio_buffer_initialize + PROTO((FILE *, int, void (*) (struct buffer *))); +extern struct buffer *compress_buffer_initialize + PROTO((struct buffer *, int, int, void (*) (struct buffer *))); +extern int buf_empty_p PROTO((struct buffer *)); +extern void buf_output PROTO((struct buffer *, const char *, int)); +extern void buf_output0 PROTO((struct buffer *, const char *)); +extern void buf_append_char PROTO((struct buffer *, int)); +extern int buf_send_output PROTO((struct buffer *)); +extern int buf_flush PROTO((struct buffer *, int)); +extern int set_nonblock PROTO((struct buffer *)); +extern int set_block PROTO((struct buffer *)); +extern int buf_send_counted PROTO((struct buffer *)); +extern int buf_send_special_count PROTO((struct buffer *, int)); +extern void buf_append_data PROTO((struct buffer *, + struct buffer_data *, + struct buffer_data *)); +extern int buf_read_file PROTO((FILE *, long, struct buffer_data **, + struct buffer_data **)); +extern int buf_read_file_to_eof PROTO((FILE *, struct buffer_data **, + struct buffer_data **)); +extern int buf_input_data PROTO((struct buffer *, int *)); +extern int buf_read_line PROTO((struct buffer *, char **, int *)); +extern int buf_read_data PROTO((struct buffer *, int, char **, int *)); +extern void buf_copy_lines PROTO((struct buffer *, struct buffer *, int)); +extern int buf_copy_counted PROTO((struct buffer *, struct buffer *, int *)); +extern int buf_chain_length PROTO((struct buffer_data *)); +extern int buf_shutdown PROTO((struct buffer *)); + +#ifdef SERVER_FLOWCONTROL +extern int buf_count_mem PROTO((struct buffer *)); +#endif /* SERVER_FLOWCONTROL */ + +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ diff --git a/contrib/cvs/src/checkin.c b/contrib/cvs/src/checkin.c index e1ce9cb..0da220c 100644 --- a/contrib/cvs/src/checkin.c +++ b/contrib/cvs/src/checkin.c @@ -20,34 +20,30 @@ #include "edit.h" int -Checkin (type, file, update_dir, repository, - rcs, rev, tag, options, message, entries) +Checkin (type, finfo, rcs, rev, tag, options, message) int type; - char *file; - char *update_dir; - char *repository; + struct file_info *finfo; char *rcs; char *rev; char *tag; char *options; char *message; - List *entries; { - char fname[PATH_MAX]; + char *fname; Vers_TS *vers; int set_time; - char *fullname; - char *tocvsPath = NULL; - fullname = xmalloc (strlen (update_dir) + strlen (file) + 10); - if (update_dir[0] == '\0') - strcpy (fullname, file); - else - sprintf (fullname, "%s/%s", update_dir, file); + /* Hmm. This message goes to stdout and the "foo,v <-- foo" + message from "ci" goes to stderr. This doesn't make a whole + lot of sense, but making everything go to stdout can only be + gracefully achieved once RCS_checkin is librarified. */ + cvs_output ("Checking in ", 0); + cvs_output (finfo->fullname, 0); + cvs_output (";\n", 0); - (void) printf ("Checking in %s;\n", fullname); - (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); + fname = xmalloc (strlen (finfo->file) + 80); + (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); /* * Move the user file to a backup file, so as to preserve its @@ -55,25 +51,25 @@ Checkin (type, file, update_dir, repository, * for the checkin and checkout. */ - tocvsPath = wrap_tocvs_process_file (fullname); + tocvsPath = wrap_tocvs_process_file (finfo->file); if (!noexec) { if (tocvsPath) { copy_file (tocvsPath, fname); - if (unlink_file_dir (file) < 0) + if (unlink_file_dir (finfo->file) < 0) if (! existence_error (errno)) - error (1, errno, "cannot remove %s", file); - copy_file (tocvsPath, file); + error (1, errno, "cannot remove %s", finfo->fullname); + copy_file (tocvsPath, finfo->file); } else { - copy_file (file, fname); + copy_file (finfo->file, fname); } } - switch (RCS_checkin (rcs, NULL, message, rev, 0, 0)) + switch (RCS_checkin (rcs, NULL, message, rev, 0)) { case 0: /* everything normal */ @@ -89,13 +85,21 @@ Checkin (type, file, update_dir, repository, if (strcmp (options, "-V4") == 0) /* upgrade to V5 now */ options[0] = '\0'; + /* Reparse the RCS file, so that we can safely call + RCS_checkout. FIXME: We could probably calculate + all the changes. */ + freercsnode (&finfo->rcs); + finfo->rcs = RCS_parse (finfo->file, finfo->repository); + /* FIXME: should be checking for errors. */ - (void) RCS_checkout (rcs, "", rev, options, RUN_TTY, 0, 0); + (void) RCS_checkout (finfo->rcs, finfo->file, rev, + (char *) NULL, options, RUN_TTY, + (RCSCHECKOUTPROC) NULL, (void *) NULL); - xchmod (file, 1); - if (xcmp (file, fname) == 0) + xchmod (finfo->file, 1); + if (xcmp (finfo->file, fname) == 0) { - rename_file (fname, file); + rename_file (fname, finfo->file); /* the time was correct, so leave it alone */ set_time = 0; } @@ -107,24 +111,23 @@ Checkin (type, file, update_dir, repository, set_time = 1; } - wrap_fromcvs_process_file (file); + wrap_fromcvs_process_file (finfo->file); /* * If we want read-only files, muck the permissions here, before * getting the file time-stamp. */ - if (cvswrite == FALSE || fileattr_get (file, "_watched")) - xchmod (file, 0); + if (cvswrite == FALSE || fileattr_get (finfo->file, "_watched")) + xchmod (finfo->file, 0); - /* re-register with the new data */ - vers = Version_TS (repository, (char *) NULL, tag, (char *) NULL, - file, 1, set_time, entries, (RCSNode *) NULL); + /* Re-register with the new data. */ + vers = Version_TS (finfo, NULL, tag, NULL, 1, set_time); if (strcmp (vers->options, "-V4") == 0) vers->options[0] = '\0'; - Register (entries, file, vers->vn_rcs, vers->ts_user, + Register (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_user, vers->options, vers->tag, vers->date, (char *) 0); - history_write (type, (char *) 0, vers->vn_rcs, file, repository); - freevers_ts (&vers); + history_write (type, NULL, vers->vn_rcs, + finfo->file, finfo->repository); if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) @@ -139,7 +142,8 @@ Checkin (type, file, update_dir, repository, if (!noexec) error (1, errno, "could not check in %s -- fork failed", - fullname); + finfo->fullname); + free (fname); return (1); default: /* ci failed */ @@ -154,9 +158,10 @@ Checkin (type, file, update_dir, repository, if (!noexec) { - rename_file (fname, file); - error (0, 0, "could not check in %s", fullname); + rename_file (fname, finfo->file); + error (0, 0, "could not check in %s", finfo->fullname); } + free (fname); return (1); } @@ -167,7 +172,7 @@ Checkin (type, file, update_dir, repository, */ if (rev) { - (void) RCS_unlock (rcs, NULL, 1); + (void) RCS_unlock (finfo->rcs, NULL, 1); } #ifdef SERVER_SUPPORT @@ -175,14 +180,16 @@ Checkin (type, file, update_dir, repository, { if (set_time) /* Need to update the checked out file on the client side. */ - server_updated (file, update_dir, repository, SERVER_UPDATED, + server_updated (finfo, vers, SERVER_UPDATED, NULL, NULL); else - server_checked_in (file, update_dir, repository); + server_checked_in (finfo->file, finfo->update_dir, finfo->repository); } else #endif - mark_up_to_date (file); + mark_up_to_date (finfo->file); + freevers_ts (&vers); + free (fname); return (0); } diff --git a/contrib/cvs/src/checkout.c b/contrib/cvs/src/checkout.c index 64aa10e..5074fe4 100644 --- a/contrib/cvs/src/checkout.c +++ b/contrib/cvs/src/checkout.c @@ -36,8 +36,6 @@ #include "cvs.h" static char *findslash PROTO((char *start, char *p)); -static int build_dirs_and_chdir PROTO((char *dir, char *prepath, char *realdir, - int sticky)); static int checkout_proc PROTO((int *pargc, char **argv, char *where, char *mwhere, char *mfile, int shorten, int local_specified, char *omodule, @@ -46,18 +44,20 @@ static int safe_location PROTO((void)); static const char *const checkout_usage[] = { - "Usage:\n %s %s [-ANPcflnps] [-r rev | -D date] [-d dir] [-k kopt] modules...\n", + "Usage:\n %s %s [-ANPRcflnps] [-r rev | -D date] [-d dir]\n", + " [-j rev1] [-j rev2] [-k kopt] modules...\n", "\t-A\tReset any sticky tags/date/kopts.\n", "\t-N\tDon't shorten module paths if -d specified.\n", "\t-P\tPrune empty directories.\n", + "\t-R\tProcess directories recursively.\n", "\t-c\t\"cat\" the module database.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", "\t-n\tDo not run module program (if any).\n", - "\t-p\tCheck out files to standard output.\n", + "\t-p\tCheck out files to standard output (avoids stickiness).\n", "\t-s\tLike -c, but include module status.\n", - "\t-r rev\tCheck out revision or tag. (implies -P)\n", - "\t-D date\tCheck out revisions as of date. (implies -P)\n", + "\t-r rev\tCheck out revision or tag. (implies -P) (is sticky)\n", + "\t-D date\tCheck out revisions as of date. (implies -P) (is sticky)\n", "\t-d dir\tCheck out into dir instead of module name.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", "\t-j rev\tMerge in changes made between current revision and rev.\n", @@ -66,14 +66,15 @@ static const char *const checkout_usage[] = static const char *const export_usage[] = { - "Usage: %s %s [-NPfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n", + "Usage: %s %s [-NRfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n", "\t-N\tDon't shorten module paths if -d specified.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", + "\t-R\tProcess directories recursively (default).\n", "\t-n\tDo not run module program (if any).\n", - "\t-r rev\tCheck out revision or tag.\n", - "\t-D date\tCheck out revisions as of date.\n", - "\t-d dir\tCheck out into dir instead of module name.\n", + "\t-r rev\tExport revision or tag.\n", + "\t-D date\tExport revisions as of date.\n", + "\t-d dir\tExport into dir instead of module name.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", NULL }; @@ -88,7 +89,9 @@ static int tag_validated = 0; static char *date = NULL; static char *join_rev1 = NULL; static char *join_rev2 = NULL; +static int join_tags_validated = 0; static char *preload_update_dir = NULL; +static char *history_name = NULL; int checkout (argc, argv) @@ -116,13 +119,13 @@ checkout (argc, argv) if (strcmp (command_name, "export") == 0) { m_type = EXPORT; - valid_options = "Nnk:d:flRQqr:D:"; + valid_options = "+Nnk:d:flRQqr:D:"; valid_usage = export_usage; } else { m_type = CHECKOUT; - valid_options = "ANnk:d:flRpQqcsr:D:j:P"; + valid_options = "+ANnk:d:flRpQqcsr:D:j:P"; valid_usage = checkout_usage; } @@ -218,8 +221,7 @@ checkout (argc, argv) if (shorten == -1) shorten = 0; - if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0) - || (tag && date)) + if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0)) usage (valid_usage); if (where && pipeout) @@ -269,13 +271,13 @@ checkout (argc, argv) && supported_request ("expand-modules")); if (expand_modules) - { + { /* This is done here because we need to read responses from the server before we send the command checkout or export files. */ client_expand_modules (argc, argv, local); - } + } if (!run_module_prog) send_arg ("-n"); if (local) send_arg ("-l"); @@ -296,6 +298,8 @@ checkout (argc, argv) } if (status) send_arg("-s"); + /* Why not send -k for export? This would appear to make + remote export differ from local export. FIXME. */ if (strcmp (command_name, "export") != 0 && options != NULL && options[0] != '\0') @@ -309,16 +313,16 @@ checkout (argc, argv) option_with_arg ("-j", join_rev2); if (expand_modules) - { - client_send_expansions (local); - } + { + client_send_expansions (local, where, 1); + } else - { + { int i; for (i = 0; i < argc; ++i) - send_arg (argv[i]); + send_arg (argv[i]); client_nonexpanded_setup (); - } + } send_to_server (strcmp (command_name, "export") == 0 ? "export\012" : "co\012", @@ -342,17 +346,18 @@ checkout (argc, argv) */ if (argc > 1 && where != NULL) { - char repository[PATH_MAX]; - (void) CVS_MKDIR (where, 0777); - if (chdir (where) < 0) + if ( CVS_CHDIR (where) < 0) error (1, errno, "cannot chdir to %s", where); preload_update_dir = xstrdup (where); where = (char *) NULL; if (!isfile (CVSADM)) { - (void) sprintf (repository, "%s/%s/%s", CVSroot, CVSROOTADM, - CVSNULLREPOS); + char *repository; + + repository = xmalloc (strlen (CVSroot_directory) + 80); + (void) sprintf (repository, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSNULLREPOS); if (!isfile (repository)) { mode_t omask; @@ -365,8 +370,8 @@ checkout (argc, argv) if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", where, repository, - (char *) NULL, (char *) NULL); + Create_Admin (".", preload_update_dir, repository, + (char *) NULL, (char *) NULL, 0); if (!noexec) { FILE *fp; @@ -379,9 +384,25 @@ checkout (argc, argv) server_set_entstat (preload_update_dir, repository); #endif } + free (repository); } } + /* If we will be calling history_write, work out the name to pass + it. */ + if (strcmp (command_name, "export") != 0 && !pipeout) + { + if (tag && date) + { + history_name = xmalloc (strlen (tag) + strlen (date) + 2); + sprintf (history_name, "%s:%s", tag, date); + } + else if (tag) + history_name = tag; + else + history_name = date; + } + /* * if where was specified (-d) and we have not taken care of it already * with the multiple arg stuff, and it was not a simple directory name @@ -396,7 +417,7 @@ checkout (argc, argv) slash = strrchr (where, '/'); *slash = '\0'; - if (chdir (where) < 0) + if ( CVS_CHDIR (where) < 0) error (1, errno, "cannot chdir to %s", where); preload_update_dir = xstrdup (where); @@ -417,25 +438,102 @@ checkout (argc, argv) static int safe_location () { - char current[PATH_MAX]; + char *current; char hardpath[PATH_MAX+5]; + size_t hardpath_len; int x; - - x = readlink(CVSroot, hardpath, sizeof hardpath - 1); + int retval; + +#ifdef HAVE_READLINK + /* FIXME-arbitrary limit: should be retrying this like xgetwd. + But how does readlink let us know that the buffer was too small? + (by returning sizeof hardpath - 1?). */ + x = readlink(CVSroot_directory, hardpath, sizeof hardpath - 1); +#else + x = -1; +#endif if (x == -1) { - strcpy(hardpath, CVSroot); + strcpy(hardpath, CVSroot_directory); } else { hardpath[x] = '\0'; } - getwd (current); - if (strncmp(current, hardpath, strlen(hardpath)) == 0) + current = xgetwd (); + hardpath_len = strlen (hardpath); + if (strlen (current) >= hardpath_len + && strncmp (current, hardpath, hardpath_len) == 0) { - return (0); + if (/* Current is a subdirectory of hardpath. */ + current[hardpath_len] == '/' + + /* Current is hardpath itself. */ + || current[hardpath_len] == '\0') + retval = 0; + else + /* It isn't a problem. For example, current is + "/foo/cvsroot-bar" and hardpath is "/foo/cvsroot". */ + retval = 1; + } + else + retval = 1; + free (current); + return retval; +} + +struct dir_to_build +{ + /* What to put in CVS/Repository. */ + char *repository; + /* The path to the directory. */ + char *dirpath; + + struct dir_to_build *next; +}; + +static int build_dirs_and_chdir PROTO ((struct dir_to_build *list, + int sticky)); + +static void build_one_dir PROTO ((char *, char *, int)); + +static void +build_one_dir (repository, dirpath, sticky) + char *repository; + char *dirpath; + int sticky; +{ + FILE *fp; + + if (!isfile (CVSADM) && strcmp (command_name, "export") != 0) + { + /* I suspect that this check could be omitted. */ + if (!isdir (repository)) + error (1, 0, "there is no repository %s", repository); + + Create_Admin (".", dirpath, repository, + sticky ? (char *) NULL : tag, + sticky ? (char *) NULL : date, + + /* FIXME? This is a guess. If it is important + for nonbranch to be set correctly here I + think we need to write it one way now and + then rewrite it later via WriteTag, once + we've had a chance to call RCS_nodeisbranch + on each file. */ + 0); + + if (!noexec) + { + fp = open_file (CVSADM_ENTSTAT, "w+"); + if (fclose (fp) == EOF) + error (1, errno, "cannot close %s", CVSADM_ENTSTAT); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_entstat (dirpath, repository); +#endif + } } - return (1); } /* @@ -459,11 +557,10 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, int which; char *cp; char *cp2; - char repository[PATH_MAX]; - char xwhere[PATH_MAX]; + char *repository; + char *xwhere = NULL; char *oldupdate = NULL; char *prepath; - char *realdirs; /* * OK, so we're doing the checkout! Our args are as follows: @@ -476,7 +573,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ /* set up the repository (maybe) for the bottom directory */ - (void) sprintf (repository, "%s/%s", CVSroot, argv[0]); + repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) + 5); + (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); /* save the original value of preload_update_dir */ if (preload_update_dir != NULL) @@ -485,12 +583,14 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, /* fix up argv[] for the case of partial modules */ if (mfile != NULL) { - char file[PATH_MAX]; + char *file; /* if mfile is really a path, straighten it out first */ if ((cp = strrchr (mfile, '/')) != NULL) { *cp = 0; + repository = xrealloc (repository, + strlen (repository) + strlen (mfile) + 10); (void) strcat (repository, "/"); (void) strcat (repository, mfile); @@ -507,9 +607,15 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (!shorten) { if (where != NULL) + { + xwhere = xmalloc (strlen (where) + strlen (mfile) + 5); (void) sprintf (xwhere, "%s/%s", where, mfile); + } else + { + xwhere = xmalloc (strlen (mwhere) + strlen (mfile) + 5); (void) sprintf (xwhere, "%s/%s", mwhere, mfile); + } where = xwhere; } else @@ -524,6 +630,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, mfile = cp + 1; } + file = xmalloc (strlen (repository) + strlen (mfile) + 5); (void) sprintf (file, "%s/%s", repository, mfile); if (isdir (file)) { @@ -532,7 +639,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, * The portion of a module was a directory, so kludge up where to * be the subdir, and fix up repository */ - (void) strcpy (repository, file); + free (repository); + repository = xstrdup (file); /* * At this point, if shorten is not enabled, we make where either @@ -545,9 +653,15 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (!shorten) { if (where != NULL) + { + xwhere = xmalloc (strlen (where) + strlen (mfile) + 5); (void) sprintf (xwhere, "%s/%s", where, mfile); + } else + { + xwhere = xmalloc (strlen (mwhere) + strlen (mfile) + 5); (void) sprintf (xwhere, "%s/%s", mwhere, mfile); + } where = xwhere; } else if (where == NULL) @@ -563,6 +677,10 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ for (i = 1; i < *pargc; i++)/* free the old ones */ free (argv[i]); + /* FIXME: Normally one has to realloc argv to make sure + argv[1] exists. But this argv does not points to the + beginning of an allocated block. For now we allocate + at least 4 entries for argv (in line2argv). */ argv[1] = xstrdup (mfile); /* set up the new one */ *pargc = 2; @@ -570,6 +688,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (where == NULL) where = mwhere; } + free (file); } /* @@ -580,7 +699,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, { if ((cp = strrchr (argv[0], '/')) != NULL) { - (void) strcpy (xwhere, cp + 1); + xwhere = xstrdup (cp + 1); where = xwhere; } } @@ -592,18 +711,18 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, where = mwhere; else { - (void) strcpy (xwhere, argv[0]); + xwhere = xstrdup (argv[0]); where = xwhere; } } if (preload_update_dir != NULL) { - char tmp[PATH_MAX]; - - (void) sprintf (tmp, "%s/%s", preload_update_dir, where); - free (preload_update_dir); - preload_update_dir = xstrdup (tmp); + preload_update_dir = + xrealloc (preload_update_dir, + strlen (preload_update_dir) + strlen (where) + 5); + strcat (preload_update_dir, "/"); + strcat (preload_update_dir, where); } else preload_update_dir = xstrdup (where); @@ -619,24 +738,80 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ if (!pipeout) { - - /* - * We need to tell build_dirs not only the path we want it to build, - * but also the repositories we want it to populate the path with. To - * accomplish this, we pass build_dirs a ``real path'' with valid - * repositories and a string to pre-pend based on how many path - * elements exist in where. Big Black Magic - */ + size_t root_len; + struct dir_to_build *head; + + /* We need to tell build_dirs not only the path we want it to + build, but also the repositories we want it to populate the + path with. To accomplish this, we walk the path backwards, + one pathname component at a time, constucting a linked + list of struct dir_to_build. */ prepath = xstrdup (repository); + + /* We don't want to start stripping elements off prepath after we + get to CVSROOT. */ + root_len = strlen (CVSroot_directory); + if (strncmp (repository, CVSroot_directory, root_len) != 0) + error (1, 0, "\ +internal error: %s doesn't start with %s in checkout_proc", + repository, CVSroot_directory); + + /* We always create at least one directory, which corresponds to + the entire strings for WHERE and REPOSITORY. */ + head = (struct dir_to_build *) xmalloc (sizeof (struct dir_to_build)); + /* Special marker to indicate that we don't want build_dirs_and_chdir + to create the CVSADM directory for us. */ + head->repository = NULL; + head->dirpath = xstrdup (where); + head->next = NULL; + cp = strrchr (where, '/'); cp2 = strrchr (prepath, '/'); + while (cp != NULL) { + struct dir_to_build *new; + new = (struct dir_to_build *) + xmalloc (sizeof (struct dir_to_build)); + new->dirpath = xmalloc (strlen (where)); + strncpy (new->dirpath, where, cp - where); + new->dirpath[cp - where] = '\0'; + if (cp2 == NULL || cp2 < prepath + root_len) + { + /* Don't walk up past CVSROOT; instead put in CVSNULLREPOS. */ + new->repository = + xmalloc (strlen (CVSroot_directory) + 80); + (void) sprintf (new->repository, "%s/%s/%s", + CVSroot_directory, + CVSROOTADM, CVSNULLREPOS); + if (!isfile (new->repository)) + { + mode_t omask; + omask = umask (cvsumask); + if (CVS_MKDIR (new->repository, 0777) < 0) + error (0, errno, "cannot create %s", + new->repository); + (void) umask (omask); + } + } + else + { + new->repository = xmalloc (strlen (prepath)); + strncpy (new->repository, prepath, cp2 - prepath); + new->repository[cp2 - prepath] = '\0'; + } + new->next = head; + head = new; + cp = findslash (where, cp - 1); cp2 = findslash (prepath, cp2 - 1); } - *cp2 = '\0'; - realdirs = cp2 + 1; + + /* First build the top-level CVSADM directory. The value we + pass in here for repository is probably wrong; see modules3-7f + in the testsuite. */ + build_one_dir (head->repository != NULL ? head->repository : prepath, + ".", *pargc <= 1); /* * build dirs on the path if necessary and leave us in the bottom @@ -644,13 +819,12 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, * subdir yet, but all the others contain CVS and Entries.Static * files */ - if (build_dirs_and_chdir (where, prepath, realdirs, *pargc <= 1) != 0) + if (build_dirs_and_chdir (head, *pargc <= 1) != 0) { error (0, 0, "ignoring module %s", omodule); free (prepath); - free (preload_update_dir); - preload_update_dir = oldupdate; - return (1); + err = 1; + goto out; } /* clean up */ @@ -667,8 +841,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", where, repository, - (char *) NULL, (char *) NULL); + Create_Admin (".", preload_update_dir, repository, + (char *) NULL, (char *) NULL, 0); fp = open_file (CVSADM_ENTSTAT, "w+"); if (fclose(fp) == EOF) error(1, errno, "cannot close %s", CVSADM_ENTSTAT); @@ -683,7 +857,15 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", where, repository, tag, date); + Create_Admin (".", preload_update_dir, repository, tag, date, + + /* FIXME? This is a guess. If it is important + for nonbranch to be set correctly here I + think we need to write it one way now and + then rewrite it later via WriteTag, once + we've had a chance to call RCS_nodeisbranch + on each file. */ + 0); } } else @@ -698,9 +880,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, repos, repository); error (0, 0, "ignoring module %s", omodule); free (repos); - free (preload_update_dir); - preload_update_dir = oldupdate; - return (1); + err = 1; + goto out; } free (repos); } @@ -713,12 +894,11 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ if (pipeout) { - if (chdir (repository) < 0) + if ( CVS_CHDIR (repository) < 0) { error (0, errno, "cannot chdir to %s", repository); - free (preload_update_dir); - preload_update_dir = oldupdate; - return (1); + err = 1; + goto out; } which = W_REPOS; if (tag != NULL && !tag_validated) @@ -738,11 +918,19 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, } } - if (tag != NULL || date != NULL) + if (tag != NULL || date != NULL || join_rev1 != NULL) which |= W_ATTIC; - /* FIXME: We don't call tag_check_valid on join_rev1 and join_rev2 - yet (make sure to handle ':' correctly if we do, though). */ + if (! join_tags_validated) + { + if (join_rev1 != NULL) + tag_check_valid_join (join_rev1, *pargc - 1, argv + 1, 0, aflag, + repository); + if (join_rev2 != NULL) + tag_check_valid_join (join_rev2, *pargc - 1, argv + 1, 0, aflag, + repository); + join_tags_validated = 1; + } /* * if we are going to be recursive (building dirs), go ahead and call the @@ -752,16 +940,17 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (!(local_specified || *pargc > 1)) { if (strcmp (command_name, "export") != 0 && !pipeout) - history_write ('O', preload_update_dir, tag ? tag : date, where, + history_write ('O', preload_update_dir, history_name, where, + repository); + else if (strcmp (command_name, "export") == 0 && !pipeout) + history_write ('E', preload_update_dir, tag ? tag : date, where, repository); err += do_update (0, (char **) NULL, options, tag, date, force_tag_match, 0 /* !local */ , 1 /* update -d */ , aflag, checkout_prune_dirs, pipeout, which, join_rev1, join_rev2, preload_update_dir); - free (preload_update_dir); - preload_update_dir = oldupdate; - return (err); + goto out; } if (!pipeout) @@ -774,22 +963,34 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, for (i = 1; i < *pargc; i++) { char *line; - char *user; Vers_TS *vers; - - user = argv[i]; - vers = Version_TS (repository, options, tag, date, user, - force_tag_match, 0, entries, (RCSNode *) NULL); + struct file_info finfo; + + memset (&finfo, 0, sizeof finfo); + finfo.file = argv[i]; + /* Shouldn't be used, so set to arbitrary value. */ + finfo.update_dir = NULL; + finfo.fullname = argv[i]; + finfo.repository = repository; + finfo.entries = entries; + /* The rcs slot is needed to get the options from the RCS + file */ + finfo.rcs = RCS_parse (finfo.file, repository); + + vers = Version_TS (&finfo, options, tag, date, + force_tag_match, 0); if (vers->ts_user == NULL) { - line = xmalloc (strlen (user) + 15); - (void) sprintf (line, "Initial %s", user); - Register (entries, user, vers->vn_rcs ? vers->vn_rcs : "0", + line = xmalloc (strlen (finfo.file) + 15); + (void) sprintf (line, "Initial %s", finfo.file); + Register (entries, finfo.file, + vers->vn_rcs ? vers->vn_rcs : "0", line, vers->options, vers->tag, vers->date, (char *) 0); free (line); } freevers_ts (&vers); + freercsnode (&finfo.rcs); } Entries_Close (entries); @@ -797,7 +998,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, /* Don't log "export", just regular "checkouts" */ if (strcmp (command_name, "export") != 0 && !pipeout) - history_write ('O', preload_update_dir, (tag ? tag : date), where, + history_write ('O', preload_update_dir, history_name, where, repository); /* go ahead and call update now that everything is set */ @@ -805,8 +1006,12 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, force_tag_match, local_specified, 1 /* update -d */, aflag, checkout_prune_dirs, pipeout, which, join_rev1, join_rev2, preload_update_dir); +out: free (preload_update_dir); preload_update_dir = oldupdate; + if (xwhere != NULL) + free (xwhere); + free (repository); return (err); } @@ -817,73 +1022,49 @@ findslash (start, p) { while (p >= start && *p != '/') p--; + /* FIXME: indexing off the start of the array like this is *NOT* + OK according to ANSI, and will break some of the time on certain + segmented architectures. */ if (p < start) return (NULL); else return (p); } -/* - * build all the dirs along the path to dir with CVS subdirs with appropriate - * repositories and Entries.Static files - */ +/* Build all the dirs along the path to DIRS with CVS subdirs with appropriate + repositories. If ->repository is NULL, do not create a CVSADM directory + for that subdirectory; just CVS_CHDIR into it. */ static int -build_dirs_and_chdir (dir, prepath, realdir, sticky) - char *dir; - char *prepath; - char *realdir; +build_dirs_and_chdir (dirs, sticky) + struct dir_to_build *dirs; int sticky; { - FILE *fp; - char repository[PATH_MAX]; - char path[PATH_MAX]; - char path2[PATH_MAX]; - char *slash; - char *slash2; - char *cp; - char *cp2; + int retval = 0; + struct dir_to_build *nextdir; - (void) strcpy (path, dir); - (void) strcpy (path2, realdir); - for (cp = path, cp2 = path2; - (slash = strchr (cp, '/')) != NULL && (slash2 = strchr (cp2, '/')) != NULL; - cp = slash + 1, cp2 = slash2 + 1) + while (dirs != NULL) { - *slash = '\0'; - *slash2 = '\0'; - (void) CVS_MKDIR (cp, 0777); - if (chdir (cp) < 0) + char *dir = last_component (dirs->dirpath); + + mkdir_if_needed (dir); + Subdir_Register (NULL, NULL, dir); + if (CVS_CHDIR (dir) < 0) { - error (0, errno, "cannot chdir to %s", cp); - return (1); + error (0, errno, "cannot chdir to %s", dir); + retval = 1; + goto out; } - if (!isfile (CVSADM) && strcmp (command_name, "export") != 0) + if (dirs->repository != NULL) { - (void) sprintf (repository, "%s/%s", prepath, path2); - /* I'm not sure whether this check is redundant. */ - if (!isdir (repository)) - error (1, 0, "there is no repository %s", repository); - Create_Admin (".", path, repository, sticky ? (char *) NULL : tag, - sticky ? (char *) NULL : date); - if (!noexec) - { - fp = open_file (CVSADM_ENTSTAT, "w+"); - if (fclose(fp) == EOF) - error(1, errno, "cannot close %s", CVSADM_ENTSTAT); -#ifdef SERVER_SUPPORT - if (server_active) - server_set_entstat (path, repository); -#endif - } + build_one_dir (dirs->repository, dirs->dirpath, sticky); + free (dirs->repository); } - *slash = '/'; - *slash2 = '/'; + nextdir = dirs->next; + free (dirs->dirpath); + free (dirs); + dirs = nextdir; } - (void) CVS_MKDIR (cp, 0777); - if (chdir (cp) < 0) - { - error (0, errno, "cannot chdir to %s", cp); - return (1); - } - return (0); + + out: + return retval; } diff --git a/contrib/cvs/src/classify.c b/contrib/cvs/src/classify.c index 924314b..55851f7 100644 --- a/contrib/cvs/src/classify.c +++ b/contrib/cvs/src/classify.c @@ -21,34 +21,23 @@ static void sticky_ck PROTO((char *file, int aflag, Vers_TS * vers, List * entri * Classify the state of a file */ Ctype -Classify_File (file, tag, date, options, force_tag_match, aflag, repository, - entries, rcsnode, versp, update_dir, pipeout) - char *file; +Classify_File (finfo, tag, date, options, force_tag_match, aflag, versp, + pipeout) + struct file_info *finfo; char *tag; char *date; char *options; int force_tag_match; int aflag; - char *repository; - List *entries; - RCSNode *rcsnode; Vers_TS **versp; - char *update_dir; int pipeout; { Vers_TS *vers; Ctype ret; - char *fullname; - - fullname = xmalloc (strlen (update_dir) + strlen (file) + 10); - if (update_dir[0] == '\0') - strcpy (fullname, file); - else - sprintf (fullname, "%s/%s", update_dir, file); /* get all kinds of good data about the file */ - vers = Version_TS (repository, options, tag, date, file, - force_tag_match, 0, entries, rcsnode); + vers = Version_TS (finfo, options, tag, date, + force_tag_match, 0); if (vers->vn_user == NULL) { @@ -59,35 +48,39 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, if (vers->ts_user == NULL) { /* there is no user file */ + /* FIXME: Why do we skip this message if vers->tag or + vers->date is set? It causes "cvs update -r tag98 foo" + to silently do nothing, which is seriously confusing + behavior. "cvs update foo" gives this message, which + is what I would expect. */ if (!force_tag_match || !(vers->tag || vers->date)) if (!really_quiet) - error (0, 0, "nothing known about %s", fullname); + error (0, 0, "nothing known about %s", finfo->fullname); ret = T_UNKNOWN; } else { /* there is a user file */ + /* FIXME: Why do we skip this message if vers->tag or + vers->date is set? It causes "cvs update -r tag98 foo" + to silently do nothing, which is seriously confusing + behavior. "cvs update foo" gives this message, which + is what I would expect. */ if (!force_tag_match || !(vers->tag || vers->date)) if (!really_quiet) error (0, 0, "use `cvs add' to create an entry for %s", - fullname); + finfo->fullname); ret = T_UNKNOWN; } } else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) { if (vers->ts_user == NULL) - /* - * Logically seems to me this should be T_UPTODATE. - * But the joining code in update.c seems to expect - * T_CHECKOUT, and that is what has traditionally been - * returned for this case. - */ - ret = T_CHECKOUT; + ret = T_UPTODATE; else { error (0, 0, "use `cvs add' to create an entry for %s", - fullname); + finfo->fullname); ret = T_UNKNOWN; } } @@ -115,13 +108,12 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, * conflict list, only if it is indeed different from what we * plan to extract */ - else if (No_Difference (file, vers, entries, - repository, update_dir)) + else if (No_Difference (finfo, vers)) { /* the files were different so it is a conflict */ if (!really_quiet) error (0, 0, "move away %s; it is in the way", - fullname); + finfo->fullname); ret = T_CONFLICT; } else @@ -141,7 +133,7 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, * entry */ if (!really_quiet) - error (0, 0, "warning: new-born %s has disappeared", fullname); + error (0, 0, "warning: new-born %s has disappeared", finfo->fullname); ret = T_REMOVE_ENTRY; } else @@ -166,7 +158,7 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, error (0, 0, "\ conflict: %s has been added, but already exists", - fullname); + finfo->fullname); } else { @@ -178,7 +170,7 @@ conflict: %s has been added, but already exists", error (0, 0, "\ conflict: %s created independently by second party", - fullname); + finfo->fullname); } ret = T_CONFLICT; } @@ -190,13 +182,10 @@ conflict: %s created independently by second party", if (vers->ts_user == NULL) { - char tmp[PATH_MAX]; - /* There is no user file (as it should be) */ - (void) sprintf (tmp, "-%s", vers->vn_rcs ? vers->vn_rcs : ""); - - if (vers->vn_rcs == NULL) + if (vers->vn_rcs == NULL + || RCS_isdead (vers->srcfile, vers->vn_rcs)) { /* @@ -205,8 +194,9 @@ conflict: %s created independently by second party", */ ret = T_REMOVE_ENTRY; } - else if (strcmp (tmp, vers->vn_user) == 0) - + else if (vers->vn_rcs == NULL + ? vers->vn_user[1] == '\0' + : strcmp (vers->vn_rcs, vers->vn_user + 1) == 0) /* * The RCS file is the same version as the user file was, and * that's OK; remove it @@ -222,7 +212,7 @@ conflict: %s created independently by second party", if (!really_quiet) error (0, 0, "conflict: removed %s was modified by second party", - fullname); + finfo->fullname); ret = T_CONFLICT; } } @@ -231,7 +221,7 @@ conflict: %s created independently by second party", /* The user file shouldn't be there */ if (!really_quiet) error (0, 0, "%s should be removed and is still there", - fullname); + finfo->fullname); ret = T_REMOVED; } } @@ -247,7 +237,7 @@ conflict: %s created independently by second party", /* There is no user file, so just remove the entry */ if (!really_quiet) error (0, 0, "warning: %s is not (any longer) pertinent", - fullname); + finfo->fullname); ret = T_REMOVE_ENTRY; } else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) @@ -259,7 +249,7 @@ conflict: %s created independently by second party", */ if (!really_quiet) error (0, 0, "%s is no longer in the repository", - fullname); + finfo->fullname); ret = T_REMOVE_ENTRY; } else @@ -268,14 +258,13 @@ conflict: %s created independently by second party", * The user file has been modified and since it is no longer * in the repository, a conflict is raised */ - if (No_Difference (file, vers, entries, - repository, update_dir)) + if (No_Difference (finfo, vers)) { /* they are different -> conflict */ if (!really_quiet) error (0, 0, "conflict: %s is modified but no longer in the repository", - fullname); + finfo->fullname); ret = T_CONFLICT; } else @@ -284,7 +273,7 @@ conflict: %s created independently by second party", if (!really_quiet) error (0, 0, "warning: %s is not (any longer) pertinent", - fullname); + finfo->fullname); ret = T_REMOVE_ENTRY; } } @@ -302,7 +291,7 @@ conflict: %s created independently by second party", */ if (strcmp (command_name, "update") == 0) if (!really_quiet) - error (0, 0, "warning: %s was lost", fullname); + error (0, 0, "warning: %s was lost", finfo->fullname); ret = T_CHECKOUT; } else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) @@ -320,10 +309,10 @@ conflict: %s created independently by second party", else { #ifdef SERVER_SUPPORT - sticky_ck (file, aflag, vers, entries, - repository, update_dir); + sticky_ck (finfo->file, aflag, vers, finfo->entries, + finfo->repository, finfo->update_dir); #else - sticky_ck (file, aflag, vers, entries); + sticky_ck (finfo->file, aflag, vers, finfo->entries); #endif ret = T_UPTODATE; } @@ -335,8 +324,7 @@ conflict: %s created independently by second party", * The user file appears to have been modified, but we call * No_Difference to verify that it really has been modified */ - if (No_Difference (file, vers, entries, - repository, update_dir)) + if (No_Difference (finfo, vers)) { /* @@ -352,10 +340,10 @@ conflict: %s created independently by second party", #else ret = T_MODIFIED; #ifdef SERVER_SUPPORT - sticky_ck (file, aflag, vers, entries, - repository, update_dir); + sticky_ck (finfo->file, aflag, vers, finfo->entries, + finfo->repository, finfo->update_dir); #else - sticky_ck (file, aflag, vers, entries); + sticky_ck (finfo->file, aflag, vers, finfo->entries); #endif /* SERVER_SUPPORT */ #endif } @@ -390,7 +378,7 @@ conflict: %s created independently by second party", if (strcmp (command_name, "update") == 0) if (!really_quiet) - error (0, 0, "warning: %s was lost", fullname); + error (0, 0, "warning: %s was lost", finfo->fullname); ret = T_CHECKOUT; } else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) @@ -413,8 +401,7 @@ conflict: %s created independently by second party", } else { - if (No_Difference (file, vers, entries, - repository, update_dir)) + if (No_Difference (finfo, vers)) /* really modified, needs to merge */ ret = T_NEEDS_MERGE; #ifdef SERVER_SUPPORT @@ -442,8 +429,6 @@ conflict: %s created independently by second party", else freevers_ts (&vers); - free (fullname); - /* return the status of the file */ return (ret); } diff --git a/contrib/cvs/src/client.c b/contrib/cvs/src/client.c index c0557ce..49f7ac8 100644 --- a/contrib/cvs/src/client.c +++ b/contrib/cvs/src/client.c @@ -4,15 +4,17 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ +#include <assert.h> #include "cvs.h" #include "getline.h" #include "edit.h" +#include "buffer.h" #ifdef CLIENT_SUPPORT #include "md5.h" -#if defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || USE_DIRECT_TCP +#if defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || defined(SOCK_ERRNO) || defined(SOCK_STRERROR) # ifdef HAVE_WINSOCK_H # include <winsock.h> # else /* No winsock.h */ @@ -20,13 +22,32 @@ # include <netinet/in.h> # include <netdb.h> # endif /* No winsock.h */ -#endif /* defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || USE_DIRECT_TCP */ +#endif -#ifdef AUTH_CLIENT_SUPPORT -char *get_cvs_password PROTO((char *user, char *host, char *cvsrooot)); -#endif /* AUTH_CLIENT_SUPPORT */ +/* If SOCK_ERRNO is defined, then send()/recv() and other socket calls + do not set errno, but that this macro should be used to obtain an + error code. This probably doesn't make sense unless + NO_SOCKET_TO_FD is also defined. */ +#ifndef SOCK_ERRNO +#define SOCK_ERRNO errno +#endif -#if HAVE_KERBEROS || USE_DIRECT_TCP +/* If SOCK_STRERROR is defined, then the error codes returned by + socket operations are not known to strerror, and this macro must be + used instead to convert those error codes to strings. */ +#ifndef SOCK_STRERROR +# define SOCK_STRERROR strerror + +# if STDC_HEADERS +# include <string.h> +# endif + +# ifndef strerror +extern char *strerror (); +# endif +#endif /* ! SOCK_STRERROR */ + +#if HAVE_KERBEROS #define CVS_PORT 1999 #if HAVE_KERBEROS @@ -36,9 +57,14 @@ extern char *krb_realmofhost (); #ifndef HAVE_KRB_GET_ERR_TEXT #define krb_get_err_text(status) krb_err_txt[status] #endif /* HAVE_KRB_GET_ERR_TEXT */ + +/* Information we need if we are going to use Kerberos encryption. */ +static C_Block kblock; +static Key_schedule sched; + #endif /* HAVE_KERBEROS */ -#endif /* HAVE_KERBEROS || USE_DIRECT_TCP */ +#endif /* HAVE_KERBEROS */ static void add_prune_candidate PROTO((char *)); @@ -70,6 +96,7 @@ static void handle_copy_file PROTO((char *, int)); static void handle_updated PROTO((char *, int)); static void handle_merged PROTO((char *, int)); static void handle_patched PROTO((char *, int)); +static void handle_rcs_diff PROTO((char *, int)); static void handle_removed PROTO((char *, int)); static void handle_remove_entry PROTO((char *, int)); static void handle_set_static_directory PROTO((char *, int)); @@ -81,8 +108,10 @@ static void handle_set_update_prog PROTO((char *, int)); static void handle_module_expansion PROTO((char *, int)); static void handle_m PROTO((char *, int)); static void handle_e PROTO((char *, int)); +static void handle_f PROTO((char *, int)); static void handle_notified PROTO((char *, int)); +static void buf_memory_error PROTO((struct buffer *)); static size_t try_read_from_server PROTO ((char *, size_t)); #endif /* CLIENT_SUPPORT */ @@ -102,29 +131,29 @@ mode_to_string (mode) mode_t mode; #endif /* __STDC__ */ { - char buf[18], u[4], g[4], o[4]; - int i; + char buf[18], u[4], g[4], o[4]; + int i; - i = 0; - if (mode & S_IRUSR) u[i++] = 'r'; - if (mode & S_IWUSR) u[i++] = 'w'; - if (mode & S_IXUSR) u[i++] = 'x'; - u[i] = '\0'; - - i = 0; - if (mode & S_IRGRP) g[i++] = 'r'; - if (mode & S_IWGRP) g[i++] = 'w'; - if (mode & S_IXGRP) g[i++] = 'x'; - g[i] = '\0'; - - i = 0; - if (mode & S_IROTH) o[i++] = 'r'; - if (mode & S_IWOTH) o[i++] = 'w'; - if (mode & S_IXOTH) o[i++] = 'x'; - o[i] = '\0'; - - sprintf(buf, "u=%s,g=%s,o=%s", u, g, o); - return xstrdup(buf); + i = 0; + if (mode & S_IRUSR) u[i++] = 'r'; + if (mode & S_IWUSR) u[i++] = 'w'; + if (mode & S_IXUSR) u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if (mode & S_IRGRP) g[i++] = 'r'; + if (mode & S_IWGRP) g[i++] = 'w'; + if (mode & S_IXGRP) g[i++] = 'x'; + g[i] = '\0'; + + i = 0; + if (mode & S_IROTH) o[i++] = 'r'; + if (mode & S_IWOTH) o[i++] = 'w'; + if (mode & S_IXOTH) o[i++] = 'x'; + o[i] = '\0'; + + sprintf(buf, "u=%s,g=%s,o=%s", u, g, o); + return xstrdup(buf); } /* @@ -235,214 +264,379 @@ change_mode (filename, mode_string) #ifdef CLIENT_SUPPORT -/* The host part of CVSROOT. */ -static char *server_host; -/* The user part of CVSROOT */ -static char *server_user; -/* The repository part of CVSROOT. */ -static char *server_cvsroot; +int client_prune_dirs; -int client_active; +static List *ignlist = (List *) NULL; -int client_prune_dirs; +/* Buffer to write to the server. */ +static struct buffer *to_server; +/* The stream underlying to_server, if we are using a stream. */ +static FILE *to_server_fp; -static int cvsroot_parsed = 0; +/* Buffer used to read from the server. */ +static struct buffer *from_server; +/* The stream underlying from_server, if we are using a stream. */ +static FILE *from_server_fp; -static List *ignlist = (List *) NULL; +/* Process ID of rsh subprocess. */ +static int rsh_pid = -1; + + +/* This routine is called when one of the buffer routines runs out of + memory. */ -/* Set server_host and server_cvsroot. */ static void -parse_cvsroot () +buf_memory_error (buf) + struct buffer *buf; { - char *p; -#ifdef AUTH_CLIENT_SUPPORT - static char *access_method; -#endif /* AUTH_CLIENT_SUPPORT */ + error (1, 0, "out of memory"); +} + +/* We want to be able to log data sent between us and the server. We + do it using log buffers. Each log buffer has another buffer which + handles the actual I/O, and a file to log information to. - /* Don't go through the trouble twice. */ - if (cvsroot_parsed) - return; + This structure is the closure field of a log buffer. */ - server_host = xstrdup (CVSroot); +struct log_buffer +{ + /* The underlying buffer. */ + struct buffer *buf; + /* The file to log information to. */ + FILE *log; +}; -#ifdef AUTH_CLIENT_SUPPORT - if ((server_host[0] == ':')) - { - /* Access method specified, as in - * "cvs -d :pserver:user@host:/path". - * We need to get past that part of CVSroot before parsing the - * rest of it. - */ - access_method = p = &(server_host[1]); +static struct buffer *log_buffer_initialize + PROTO((struct buffer *, FILE *, int, void (*) (struct buffer *))); +static int log_buffer_input PROTO((void *, char *, int, int, int *)); +static int log_buffer_output PROTO((void *, const char *, int, int *)); +static int log_buffer_flush PROTO((void *)); +static int log_buffer_block PROTO((void *, int)); +static int log_buffer_shutdown PROTO((void *)); + +/* Create a log buffer. */ + +static struct buffer * +log_buffer_initialize (buf, fp, input, memory) + struct buffer *buf; + FILE *fp; + int input; + void (*memory) PROTO((struct buffer *)); +{ + struct log_buffer *n; + + n = (struct log_buffer *) xmalloc (sizeof *n); + n->buf = buf; + n->log = fp; + return buf_initialize (input ? log_buffer_input : NULL, + input ? NULL : log_buffer_output, + input ? NULL : log_buffer_flush, + log_buffer_block, + log_buffer_shutdown, + memory, + n); +} - if (! *access_method) - error (1, 0, "bad CVSroot: %s", CVSroot); +/* The input function for a log buffer. */ - if (! *(p = strchr (access_method, ':'))) - error (1, 0, "bad CVSroot: %s", CVSroot); - - *p = '\0'; - p++; +static int +log_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + int status; + size_t n_to_write; - server_host = p; - - if (! *server_host) - error (1, 0, "bad CVSroot: %s", CVSroot); + if (lb->buf->input == NULL) + abort (); - if (strcmp (access_method, "pserver") == 0) - use_authenticating_server = 1; - else - error (1, 0, "unknown access method: %s", access_method); - } -#endif /* AUTH_CLIENT_SUPPORT */ - - /* First get just the pathname. */ - server_cvsroot = strchr (server_host, ':'); - *server_cvsroot = '\0'; - ++server_cvsroot; - - /* Then deal with host and possible user. */ - if ( (p = strchr (server_host, '@')) == NULL) + status = (*lb->buf->input) (lb->buf->closure, data, need, size, got); + if (status != 0) + return status; + + if (*got > 0) { - server_user = NULL; + n_to_write = *got; + if (fwrite (data, 1, n_to_write, lb->log) != n_to_write) + error (0, errno, "writing to log file"); } - else + + return 0; +} + +/* The output function for a log buffer. */ + +static int +log_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + int status; + size_t n_to_write; + + if (lb->buf->output == NULL) + abort (); + + status = (*lb->buf->output) (lb->buf->closure, data, have, wrote); + if (status != 0) + return status; + + if (*wrote > 0) { - server_user = server_host; - server_host = p; - ++server_host; - *p = '\0'; + n_to_write = *wrote; + if (fwrite (data, 1, n_to_write, lb->log) != n_to_write) + error (0, errno, "writing to log file"); } - - client_active = 1; - cvsroot_parsed = 1; + + return 0; +} + +/* The flush function for a log buffer. */ + +static int +log_buffer_flush (closure) + void *closure; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + + if (lb->buf->flush == NULL) + abort (); + + /* We don't really have to flush the log file here, but doing it + will let tail -f on the log file show what is sent to the + network as it is sent. */ + if (fflush (lb->log) != 0) + error (0, errno, "flushing log file"); + + return (*lb->buf->flush) (lb->buf->closure); +} + +/* The block function for a log buffer. */ + +static int +log_buffer_block (closure, block) + void *closure; + int block; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + + if (block) + return set_block (lb->buf); + else + return set_nonblock (lb->buf); +} + +/* The shutdown function for a log buffer. */ + +static int +log_buffer_shutdown (closure) + void *closure; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + + return buf_shutdown (lb->buf); } #ifdef NO_SOCKET_TO_FD + /* Under certain circumstances, we must communicate with the server via a socket using send() and recv(). This is because under some operating systems (OS/2 and Windows 95 come to mind), a socket cannot be converted to a file descriptor -- it must be treated as a - socket and nothing else. */ + socket and nothing else. + + We may also need to deal with socket routine error codes differently + in these cases. This is handled through the SOCK_ERRNO and + SOCK_STRERROR macros. */ + static int use_socket_style = 0; static int server_sock; -#endif /* NO_SOCKET_TO_FD */ -/* Stream to write to the server. */ -static FILE *to_server; -/* Stream to read from the server. */ -static FILE *from_server; +/* These routines implement a buffer structure which uses send and + recv. The buffer is always in blocking mode so we don't implement + the block routine. */ -/* We might want to log client/server traffic. */ -static FILE *from_server_logfile; -static FILE *to_server_logfile; +/* Note that it is important that these routines always handle errors + internally and never return a positive errno code, since it would in + general be impossible for the caller to know in general whether any + error code came from a socket routine (to decide whether to use + SOCK_STRERROR or simply strerror to print an error message). */ -#if ! RSH_NOT_TRANSPARENT -/* Process ID of rsh subprocess. */ -static int rsh_pid = -1; -#endif /* ! RSH_NOT_TRANSPARENT */ +/* We use an instance of this structure as the closure field. */ - -/* - * Read a line from the server. Result does not include the terminating \n. - * - * Space for the result is malloc'd and should be freed by the caller. - * - * Returns number of bytes read. If EOF_OK, then return 0 on end of file, - * else end of file is an error. - */ -static int -read_line (resultp, eof_ok) - char **resultp; - int eof_ok; +struct socket_buffer { - int c; - char *result; - size_t input_index = 0; - size_t result_size = 80; + /* The socket number. */ + int socket; +}; -#ifdef NO_SOCKET_TO_FD - if (! use_socket_style) -#endif /* NO_SOCKET_TO_FD */ - fflush (to_server); +static struct buffer *socket_buffer_initialize + PROTO ((int, int, void (*) (struct buffer *))); +static int socket_buffer_input PROTO((void *, char *, int, int, int *)); +static int socket_buffer_output PROTO((void *, const char *, int, int *)); +static int socket_buffer_flush PROTO((void *)); - result = (char *) xmalloc (result_size); +/* Create a buffer based on a socket. */ - while (1) - { +static struct buffer * +socket_buffer_initialize (socket, input, memory) + int socket; + int input; + void (*memory) PROTO((struct buffer *)); +{ + struct socket_buffer *n; + + n = (struct socket_buffer *) xmalloc (sizeof *n); + n->socket = socket; + return buf_initialize (input ? socket_buffer_input : NULL, + input ? NULL : socket_buffer_output, + input ? NULL : socket_buffer_flush, + (int (*) PROTO((void *, int))) NULL, + (int (*) PROTO((void *))) NULL, + memory, + n); +} -#ifdef NO_SOCKET_TO_FD - if (use_socket_style) - { - char ch; - /* Yes, this sucks performance-wise. Short of implementing - our own buffering, I'm not sure how to effect a big - improvement. We could at least avoid calling - read_from_server() for each character if we were willing - to duplicate a lot of its code, but I'm not sure that's - worth it. */ - read_from_server (&ch, 1); - c = ch; - } - else -#endif /* NO_SOCKET_TO_FD */ - c = getc (from_server); +/* The buffer input function for a buffer built on a socket. */ - if (c == EOF) - { - free (result); +static int +socket_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + struct socket_buffer *sb = (struct socket_buffer *) closure; + int nbytes; -#ifdef NO_SOCKET_TO_FD - if (! use_socket_style) -#endif /* NO_SOCKET_TO_FD */ - if (ferror (from_server)) - error (1, errno, "reading from server"); - - /* It's end of file. */ - if (eof_ok) - return 0; - else - error (1, 0, "end of file from server (consult above messages if any)"); - } + /* I believe that the recv function gives us exactly the semantics + we want. If there is a message, it returns immediately with + whatever it could get. If there is no message, it waits until + one comes in. In other words, it is not like read, which in + blocking mode normally waits until all the requested data is + available. */ - if (c == '\n') - break; - - result[input_index++] = c; - while (input_index + 1 >= result_size) + *got = 0; + + do + { + nbytes = recv (sb->socket, data, size, 0); + if (nbytes < 0) + error (1, 0, "reading from server: %s", SOCK_STRERROR (SOCK_ERRNO)); + if (nbytes == 0) { - result_size *= 2; - result = (char *) xrealloc (result, result_size); + /* End of file (for example, the server has closed + the connection). If we've already read something, we + just tell the caller about the data, not about the end of + file. If we've read nothing, we return end of file. */ + if (*got == 0) + return -1; + else + return 0; } + need -= nbytes; + size -= nbytes; + data += nbytes; + *got += nbytes; } + while (need > 0); - if (resultp) - *resultp = result; + return 0; +} - /* Terminate it just for kicks, but we *can* deal with embedded NULs. */ - result[input_index] = '\0'; +/* The buffer output function for a buffer built on a socket. */ + +static int +socket_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + struct socket_buffer *sb = (struct socket_buffer *) closure; + + *wrote = have; + +#ifdef SEND_NEVER_PARTIAL + /* If send() never will produce a partial write, then just do it. This + is needed for systems where its return value is something other than + the number of bytes written. */ + if (send (sb->socket, data, have, 0) < 0) + error (1, 0, "writing to server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); +#else + while (have > 0) + { + int nbytes; + + nbytes = send (sb->socket, data, have, 0); + if (nbytes < 0) + error (1, 0, "writing to server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); + + have -= nbytes; + data += nbytes; + } +#endif + + return 0; +} + +/* The buffer flush function for a buffer built on a socket. */ + +/*ARGSUSED*/ +static int +socket_buffer_flush (closure) + void *closure; +{ + /* Nothing to do. Sockets are always flushed. */ + return 0; +} -#ifdef NO_SOCKET_TO_FD - if (! use_socket_style) #endif /* NO_SOCKET_TO_FD */ + +/* + * Read a line from the server. Result does not include the terminating \n. + * + * Space for the result is malloc'd and should be freed by the caller. + * + * Returns number of bytes read. + */ +static int +read_line (resultp) + char **resultp; +{ + int status; + char *result; + int len; + + status = buf_flush (to_server, 1); + if (status != 0) + error (1, status, "writing to server"); + + status = buf_read_line (from_server, &result, &len); + if (status != 0) { - /* - * If we're using socket style, then everything has already - * been logged because read_from_server() was used to get the - * individual chars, and read_from_server() logs already. - */ - if (from_server_logfile) - { - if (fwrite (result, 1, input_index, from_server_logfile) - < input_index) - error (0, errno, "writing to from-server logfile"); - putc ('\n', from_server_logfile); - } + if (status == -1) + error (1, 0, "end of file from server (consult above messages if any)"); + else if (status == -2) + error (1, 0, "out of memory"); + else + error (1, status, "reading from server"); } - - if (resultp == NULL) + + if (resultp != NULL) + *resultp = result; + else free (result); - return input_index; + + return len; } #endif /* CLIENT_SUPPORT */ @@ -456,23 +650,28 @@ read_line (resultp, eof_ok) */ int gzip_level; +/* + * Level of compression to use when running gzip on a single file. + */ +int file_gzip_level; + int filter_through_gzip (fd, dir, level, pidp) - int fd, dir, level; - pid_t *pidp; + int fd, dir, level; + pid_t *pidp; { - static char buf[5] = "-"; - static char *gzip_argv[3] = { "gzip", buf }; + static char buf[5] = "-"; + static char *gzip_argv[3] = { "gzip", buf }; - sprintf (buf+1, "%d", level); - return filter_stream_through_program (fd, dir, &gzip_argv[0], pidp); + sprintf (buf+1, "%d", level); + return filter_stream_through_program (fd, dir, &gzip_argv[0], pidp); } int filter_through_gunzip (fd, dir, pidp) - int fd, dir; - pid_t *pidp; + int fd, dir; + pid_t *pidp; { - static char *gunzip_argv[3] = { "gunzip", "-d" }; - return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp); + static char *gunzip_argv[3] = { "gzip", "-d" }; + return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp); } #endif /* CLIENT_SUPPORT or SERVER_SUPPORT */ @@ -485,8 +684,10 @@ int filter_through_gunzip (fd, dir, pidp) */ static char *toplevel_repos; -/* Working directory when we first started. */ -char toplevel_wd[PATH_MAX]; +/* Working directory when we first started. Note: we could speed things + up on some systems by using savecwd.h here instead of just always + storing a name. */ +char *toplevel_wd; static void handle_ok (args, len) @@ -559,9 +760,6 @@ handle_valid_requests (args, len) */ send_to_server (rq->name, 0); send_to_server ("\012", 0); - - if (!strcmp("UseUnchanged",rq->name)) - use_unchanged = 1; } else rq->status = rq_supported; @@ -577,26 +775,10 @@ handle_valid_requests (args, len) } } -static int use_directory = -1; - -static char *get_short_pathname PROTO((const char *)); - -static char * -get_short_pathname (name) - const char *name; -{ - const char *retval; - if (use_directory) - return (char *) name; - if (strncmp (name, toplevel_repos, strlen (toplevel_repos)) != 0) - error (1, 0, "server bug: name `%s' doesn't specify file in `%s'", - name, toplevel_repos); - retval = name + strlen (toplevel_repos) + 1; - if (retval[-1] != '/') - error (1, 0, "server bug: name `%s' doesn't specify file in `%s'", - name, toplevel_repos); - return (char *) retval; -} +/* This variable holds the result of Entries_Open, so that we can + close Entries_Close on it when we move on to a new directory, or + when we finish. */ +static List *last_entries; /* * Do all the processing for PATHNAME, where pathname consists of the @@ -618,12 +800,15 @@ call_in_directory (pathname, func, data) char *filename)); char *data; { - static List *last_entries; - char *dir_name; char *filename; - /* Just the part of pathname relative to toplevel_repos. */ - char *short_pathname = get_short_pathname (pathname); + /* This is what we get when we hook up the directory (working directory + name) from PATHNAME with the filename from REPOSNAME. For example: + pathname: ccvs/src/ + reposname: /u/src/master/ccvs/foo/ChangeLog + short_pathname: ccvs/src/ChangeLog + */ + char *short_pathname; char *p; /* @@ -646,30 +831,23 @@ call_in_directory (pathname, func, data) int reposdirname_absolute; reposname = NULL; - if (use_directory) - read_line (&reposname, 0); + read_line (&reposname); + assert (reposname != NULL); reposdirname_absolute = 0; - if (reposname != NULL) + if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0) + { + reposdirname_absolute = 1; + short_repos = reposname; + } + else { - if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0) + short_repos = reposname + strlen (toplevel_repos) + 1; + if (short_repos[-1] != '/') { reposdirname_absolute = 1; short_repos = reposname; } - else - { - short_repos = reposname + strlen (toplevel_repos) + 1; - if (short_repos[-1] != '/') - { - reposdirname_absolute = 1; - short_repos = reposname; - } - } - } - else - { - short_repos = short_pathname; } reposdirname = xstrdup (short_repos); p = strrchr (reposdirname, '/'); @@ -681,7 +859,7 @@ call_in_directory (pathname, func, data) else *p = '\0'; - dir_name = xstrdup (short_pathname); + dir_name = xstrdup (pathname); p = strrchr (dir_name, '/'); if (p == NULL) { @@ -699,30 +877,66 @@ call_in_directory (pathname, func, data) else ++filename; - if (reposname != NULL) - { - /* This is the use_directory case. */ - - short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5); - strcpy (short_pathname, pathname); - strcat (short_pathname, filename); - } + short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5); + strcpy (short_pathname, pathname); + strcat (short_pathname, filename); if (last_dir_name == NULL || strcmp (last_dir_name, dir_name) != 0) { + int newdir; + + if (strcmp (command_name, "export") != 0) + if (last_entries) + Entries_Close (last_entries); + if (last_dir_name) free (last_dir_name); last_dir_name = dir_name; - if (toplevel_wd[0] == '\0') - if (getwd (toplevel_wd) == NULL) - error (1, 0, - "could not get working directory: %s", toplevel_wd); + if (toplevel_wd == NULL) + { + toplevel_wd = xgetwd (); + if (toplevel_wd == NULL) + error (1, errno, "could not get working directory"); + } - if (chdir (toplevel_wd) < 0) + if (CVS_CHDIR (toplevel_wd) < 0) error (1, errno, "could not chdir to %s", toplevel_wd); - if (chdir (dir_name) < 0) + newdir = 0; + + /* Create the CVS directory at the top level if needed. + The isdir seems like an unneeded system call, but it *does* + need to be called both if the CVS_CHDIR below succeeds (e.g. + "cvs co .") or if it fails (e.g. basicb-1a in testsuite). */ + if (/* I think the reposdirname_absolute case has to do with + things like "cvs update /foo/bar". In any event, the + code below which tries to put toplevel_repos into + CVS/Repository is almost surely unsuited to + the reposdirname_absolute case. */ + !reposdirname_absolute + + && ! isdir (CVSADM)) + { + char *repo; + char *r; + + newdir = 1; + + repo = xmalloc (strlen (toplevel_repos) + + 10); + strcpy (repo, toplevel_repos); + r = repo + strlen (repo); + if (r[-1] != '.' || r[-2] != '/') + strcpy (r, "/."); + + Create_Admin (".", ".", repo, (char *) NULL, + (char *) NULL, 0); + + free (repo); + } + + if ( CVS_CHDIR (dir_name) < 0) { char *dir; char *dirp; @@ -731,6 +945,13 @@ call_in_directory (pathname, func, data) error (1, errno, "could not chdir to %s", dir_name); /* Directory does not exist, we need to create it. */ + newdir = 1; + + /* Provided we are willing to assume that directories get + created one at a time, we could simplify this a lot. + Do note that one aspect still would need to walk the + dir_name path: the checking for "fncmp (dir, CVSADM)". */ + dir = xmalloc (strlen (dir_name) + 1); dirp = dir_name; rdirp = reposdirname; @@ -759,19 +980,21 @@ call_in_directory (pathname, func, data) { dirp = strchr (dirp, '/'); if (dirp) - { + { strncpy (dir, dir_name, dirp - dir_name); dir[dirp - dir_name] = '\0'; /* Skip the slash. */ ++dirp; if (rdirp == NULL) - error (0, 0, - "internal error: repository string too short."); + /* This just means that the repository string has + fewer components than the dir_name string. But + that is OK (e.g. see modules3-8 in testsuite). */ + ; else - rdirp = strchr (rdirp, '/'); - } + rdirp = strchr (rdirp, '/'); + } else - { + { /* If there are no more slashes in the dir name, we're down to the most nested directory -OR- to the name of a module. In the first case, we @@ -789,35 +1012,19 @@ call_in_directory (pathname, func, data) rdirp = NULL; strcpy (dir, dir_name); - } + } + + if (fncmp (dir, CVSADM) == 0) + { + error (0, 0, "cannot create a directory named %s", dir); + error (0, 0, "because CVS uses \"%s\" for its own uses", + CVSADM); + error (1, 0, "rename the directory and try again"); + } - if (CVS_MKDIR (dir, 0777) < 0) + if (mkdir_if_needed (dir)) { - /* Now, let me get this straight. In IBM C/C++ - * under OS/2, the error string for EEXIST is: - * - * "The file already exists", - * - * and the error string for EACCESS is: - * - * "The file or directory specified is read-only". - * - * Nonetheless, mkdir() will set EACCESS if the - * directory *exists*, according both to the - * documentation and its actual behavior. - * - * I'm sure that this made sense, to someone, - * somewhere, sometime. Just not me, here, now. - */ -#ifdef EACCESS - if ((errno != EACCESS) && (errno != EEXIST)) - error (1, errno, "cannot make directory %s", dir); -#else /* ! defined(EACCESS) */ - if ((errno != EEXIST)) - error (1, errno, "cannot make directory %s", dir); -#endif /* defined(EACCESS) */ - - /* It already existed, fine. Just keep going. */ + /* It already existed, fine. Just keep going. */ } else if (strcmp (command_name, "export") == 0) /* Don't create CVSADM directories if this is export. */ @@ -831,7 +1038,7 @@ call_in_directory (pathname, func, data) * relative to cvsroot. */ char *repo; - char *r; + char *r, *b; repo = xmalloc (strlen (reposdirname) + strlen (toplevel_repos) @@ -847,6 +1054,13 @@ call_in_directory (pathname, func, data) if (rdirp) { + /* See comment near start of function; the only + way that the server can put the right thing + in each CVS/Repository file is to create the + directories one at a time. I think that the + CVS server has been doing this all along. */ + error (0, 0, "\ +warning: server is not creating directories one at a time"); strncpy (r, reposdirname, rdirp - reposdirname); r[rdirp - reposdirname] = '\0'; } @@ -854,8 +1068,18 @@ call_in_directory (pathname, func, data) strcpy (r, reposdirname); Create_Admin (dir, dir, repo, - (char *)NULL, (char *)NULL); + (char *)NULL, (char *)NULL, 0); free (repo); + + b = strrchr (dir, '/'); + if (b == NULL) + Subdir_Register ((List *) NULL, (char *) NULL, dir); + else + { + *b = '\0'; + Subdir_Register ((List *) NULL, dir, b + 1); + *b = '/'; + } } if (rdirp != NULL) @@ -867,26 +1091,42 @@ call_in_directory (pathname, func, data) } while (dirp != NULL); free (dir); /* Now it better work. */ - if (chdir (dir_name) < 0) + if ( CVS_CHDIR (dir_name) < 0) error (1, errno, "could not chdir to %s", dir_name); } if (strcmp (command_name, "export") != 0) { - if (last_entries) - Entries_Close (last_entries); last_entries = Entries_Open (0); + + /* If this is a newly created directory, we will record + all subdirectory information, so call Subdirs_Known in + case there are no subdirectories. If this is not a + newly created directory, it may be an old working + directory from before we recorded subdirectory + information in the Entries file. We force a search for + all subdirectories now, to make sure our subdirectory + information is up to date. If the Entries file does + record subdirectory information, then this call only + does list manipulation. */ + if (newdir) + Subdirs_Known (last_entries); + else + { + List *dirlist; + + dirlist = Find_Directories ((char *) NULL, W_LOCAL, + last_entries); + dellist (&dirlist); + } } } else free (dir_name); free (reposdirname); (*func) (data, last_entries, short_pathname, filename); - if (reposname != NULL) - { - free (short_pathname); - free (reposname); - } + free (short_pathname); + free (reposname); } static void @@ -901,7 +1141,7 @@ copy_a_file (data, ent_list, short_pathname, filename) char *p; #endif - read_line (&newname, 0); + read_line (&newname); #ifdef USE_VMS_FILENAMES /* Mogrify the filename so VMS is happy with it. */ @@ -949,7 +1189,7 @@ read_counted_file (filename, fullname) FILE *fp; - read_line (&size_string, 0); + read_line (&size_string); if (size_string[0] == 'z') error (1, 0, "\ protocol error: compressed files not supported for that operation"); @@ -968,7 +1208,7 @@ protocol error: compressed files not supported for that operation"); is binary or not. I haven't carefully looked into whether CVS/Template files should use local text file conventions or not. */ - fp = fopen (filename, "wb"); + fp = CVS_FOPEN (filename, "wb"); if (fp == NULL) error (1, errno, "cannot write %s", fullname); nread = size; @@ -1002,6 +1242,11 @@ protocol error: compressed files not supported for that operation"); } /* + * The time stamp of the last file we registered. + */ +static time_t last_register_time; + +/* * The Checksum response gives the checksum for the file transferred * over by the next Updated, Merged or Patch response. We just store * it here, and then check it in update_entries. @@ -1082,9 +1327,23 @@ struct update_entries_data * We are getting a patch against the existing local file, not * an entire new file. */ - UPDATE_ENTRIES_PATCH + UPDATE_ENTRIES_PATCH, + /* + * We are getting an RCS change text (diff -n output) against + * the existing local file, not an entire new file. + */ + UPDATE_ENTRIES_RCS_DIFF } contents; + enum { + /* We are replacing an existing file. */ + UPDATE_ENTRIES_EXISTING, + /* We are creating a new file. */ + UPDATE_ENTRIES_NEW, + /* We don't know whether it is existing or new. */ + UPDATE_ENTRIES_EXISTING_OR_NEW + } existp; + /* * String to put in the timestamp field or NULL to use the timestamp * of the file. @@ -1109,13 +1368,13 @@ update_entries (data_arg, ent_list, short_pathname, filename) /* Timestamp field. Always empty according to the protocol. */ char *ts; char *options; - char *tag; - char *date; + char *tag = NULL; + char *date = NULL; char *tag_or_date; - char *scratch_entries; + char *scratch_entries = NULL; int bin; - read_line (&entries_line, 0); + read_line (&entries_line); /* * Parse the entries line. @@ -1149,8 +1408,6 @@ update_entries (data_arg, ent_list, short_pathname, filename) cp = strchr (tag_or_date, '/'); if (cp != NULL) *cp = '\0'; - tag = (char *) NULL; - date = (char *) NULL; if (*tag_or_date == 'T') tag = tag_or_date + 1; else if (*tag_or_date == 'D') @@ -1167,20 +1424,20 @@ update_entries (data_arg, ent_list, short_pathname, filename) options = NULL; if (data->contents == UPDATE_ENTRIES_UPDATE - || data->contents == UPDATE_ENTRIES_PATCH) + || data->contents == UPDATE_ENTRIES_PATCH + || data->contents == UPDATE_ENTRIES_RCS_DIFF) { char *size_string; char *mode_string; int size; - int fd; char *buf; char *temp_filename; - int use_gzip, gzip_status; - pid_t gzip_pid = 0; + int use_gzip; + int patch_failed; - read_line (&mode_string, 0); + read_line (&mode_string); - read_line (&size_string, 0); + read_line (&size_string); if (size_string[0] == 'z') { use_gzip = 1; @@ -1193,6 +1450,74 @@ update_entries (data_arg, ent_list, short_pathname, filename) } free (size_string); + /* Note that checking this separately from writing the file is + a race condition: if the existing or lack thereof of the + file changes between now and the actually calls which + operate on it, we lose. However (a) there are so many + cases, I'm reluctant to try to fix them all, (b) in some + cases the system might not even have a system call which + does the right thing, and (c) it isn't clear this needs to + work. */ + if (data->existp == UPDATE_ENTRIES_EXISTING + && !isfile (filename)) + /* Emit a warning and update the file anyway. */ + error (0, 0, "warning: %s unexpectedly disappeared", + short_pathname); + + if (data->existp == UPDATE_ENTRIES_NEW + && isfile (filename)) + { + /* Emit a warning and refuse to update the file; we don't want + to clobber a user's file. */ + size_t nread; + size_t toread; + + /* size should be unsigned, but until we get around to fixing + that, work around it. */ + size_t usize; + + char buf[8192]; + + /* This error might be confusing; it isn't really clear to + the user what to do about it. Keep in mind that it has + several causes: (1) something/someone creates the file + during the time that CVS is running, (2) the repository + has two files whose names clash for the client because + of case-insensitivity or similar causes, (3) a special + case of this is that a file gets renamed for example + from a.c to A.C. A "cvs update" on a case-insensitive + client will get this error. Repeating the update takes + care of the problem, but is it clear to the user what + is going on and what to do about it?, (4) the client + has a file which the server doesn't know about (e.g. "? + foo" file), and that name clashes with a file the + server does know about, (5) classify.c will print the same + message for other reasons. + + I hope the above paragraph makes it clear that making this + clearer is not a one-line fix. */ + error (0, 0, "move away %s; it is in the way", short_pathname); + + discard_file_and_return: + /* Now read and discard the file contents. */ + usize = size; + nread = 0; + while (nread < usize) + { + toread = usize - nread; + if (toread > sizeof buf) + toread = sizeof buf; + + nread += try_read_from_server (buf, toread); + if (nread == usize) + break; + } + + free (mode_string); + free (entries_line); + return; + } + temp_filename = xmalloc (strlen (filename) + 80); #ifdef USE_VMS_FILENAMES /* A VMS rename of "blah.dat" to "foo" to implies a @@ -1205,6 +1530,7 @@ update_entries (data_arg, ent_list, short_pathname, filename) sprintf (temp_filename, ".new.%s", filename); #endif /* _POSIX_NO_TRUNC */ #endif /* USE_VMS_FILENAMES */ + buf = xmalloc (size); /* Some systems, like OS/2 and Windows NT, end lines with CRLF @@ -1219,36 +1545,66 @@ update_entries (data_arg, ent_list, short_pathname, filename) else bin = 0; - fd = open (temp_filename, - O_WRONLY | O_CREAT | O_TRUNC | (bin ? OPEN_BINARY : 0), - 0777); - - if (fd < 0) - error (1, errno, "writing %s", short_pathname); + if (data->contents == UPDATE_ENTRIES_RCS_DIFF) + { + /* This is an RCS change text. We just hold the change + text in memory. */ - if (use_gzip) - fd = filter_through_gunzip (fd, 0, &gzip_pid); + if (use_gzip) + error (1, 0, + "server error: gzip invalid with RCS change text"); - if (size > 0) - { - read_from_server (buf, size); - - if (write (fd, buf, size) != size) - error (1, errno, "writing %s", short_pathname); + read_from_server (buf, size); } - - if (close (fd) < 0) - error (1, errno, "writing %s", short_pathname); - if (gzip_pid > 0) + else { - if (waitpid (gzip_pid, &gzip_status, 0) == -1) - error (1, errno, "waiting for gzip process %ld", - (long) gzip_pid); - else if (gzip_status != 0) - error (1, 0, "gzip process exited %d", gzip_status); - } + int fd; + pid_t gzip_pid = 0; + + fd = CVS_OPEN (temp_filename, + (O_WRONLY | O_CREAT | O_TRUNC + | (bin ? OPEN_BINARY : 0)), + 0777); + + if (fd < 0) + { + /* I can see a case for making this a fatal error; for + a condition like disk full or network unreachable + (for a file server), carrying on and giving an + error on each file seems unnecessary. But if it is + a permission problem, or some such, then it is + entirely possible that future files will not have + the same problem. */ + error (0, errno, "cannot write %s", short_pathname); + goto discard_file_and_return; + } + + if (use_gzip) + fd = filter_through_gunzip (fd, 0, &gzip_pid); + + if (size > 0) + { + read_from_server (buf, size); + + if (write (fd, buf, size) != size) + error (1, errno, "writing %s", short_pathname); + } + + if (close (fd) < 0) + error (1, errno, "writing %s", short_pathname); + if (gzip_pid > 0) + { + int gzip_status; - gzip_pid = -1; + if (waitpid (gzip_pid, &gzip_status, 0) == -1) + error (1, errno, "waiting for gzip process %ld", + (long) gzip_pid); + else if (gzip_status != 0) + error (1, 0, "gzip process exited %d", gzip_status); + } + + gzip_pid = -1; + } /* Since gunzip writes files without converting LF to CRLF (a reasonable behavior), we now have a patch file in LF @@ -1256,6 +1612,8 @@ update_entries (data_arg, ent_list, short_pathname, filename) it to patch; patch can handle it. However, if it's the final source file, convert it. */ + patch_failed = 0; + if (data->contents == UPDATE_ENTRIES_UPDATE) { #ifdef LINES_CRLF_TERMINATED @@ -1267,30 +1625,51 @@ update_entries (data_arg, ent_list, short_pathname, filename) { convert_file (temp_filename, O_RDONLY | OPEN_BINARY, filename, O_WRONLY | O_CREAT | O_TRUNC); - if (unlink (temp_filename) < 0) + if ( CVS_UNLINK (temp_filename) < 0) error (0, errno, "warning: couldn't delete %s", temp_filename); } else +#ifdef BROKEN_READWRITE_CONVERSION + { + /* If only stdio, not open/write/etc., do text/binary + conversion, use convert_file which can compensate + (FIXME: we could just use stdio instead which would + avoid the whole problem). */ + if (!bin) + { + convert_file (temp_filename, O_RDONLY | OPEN_BINARY, + filename, O_WRONLY | O_CREAT | O_TRUNC); + if (CVS_UNLINK (temp_filename) < 0) + error (0, errno, "warning: couldn't delete %s", + temp_filename); + } + else + rename_file (temp_filename, filename); + } +#else rename_file (temp_filename, filename); +#endif #else /* ! LINES_CRLF_TERMINATED */ rename_file (temp_filename, filename); #endif /* LINES_CRLF_TERMINATED */ } - else + else if (data->contents == UPDATE_ENTRIES_PATCH) { int retcode; - char backup[PATH_MAX]; + char *backup; struct stat s; - (void) sprintf (backup, "%s~", filename); + backup = xmalloc (strlen (filename) + 5); + strcpy (backup, filename); + strcat (backup, "~"); (void) unlink_file (backup); if (!isfile (filename)) error (1, 0, "patch original file %s does not exist", short_pathname); - if (stat (temp_filename, &s) < 0) - error (1, 1, "can't stat patch file %s", temp_filename); + if ( CVS_STAT (temp_filename, &s) < 0) + error (1, errno, "can't stat patch file %s", temp_filename); if (s.st_size == 0) retcode = 0; else @@ -1322,27 +1701,126 @@ update_entries (data_arg, ent_list, short_pathname, filename) (void) unlink_file (path_tmp); free (path_tmp); - /* Save this file to retrieve later. */ - failed_patches = - (char **) xrealloc ((char *) failed_patches, - ((failed_patches_count + 1) - * sizeof (char *))); - failed_patches[failed_patches_count] = - xstrdup (short_pathname); - ++failed_patches_count; - error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0, "could not patch %s%s", filename, retcode == -1 ? "" : "; will refetch"); - stored_checksum_valid = 0; + patch_failed = 1; + } + free (backup); + } + else + { + struct stat s; + char *filebuf, *tobuf; + size_t filebufsize; + FILE *e; + size_t nread; + char *patchedbuf; + size_t patchedlen; + + /* Handle UPDATE_ENTRIES_RCS_DIFF. */ + + if (!isfile (filename)) + error (1, 0, "patch original file %s does not exist", + short_pathname); + if (CVS_STAT (filename, &s) < 0) + error (1, errno, "can't stat %s", short_pathname); + + filebufsize = s.st_size; + filebuf = xmalloc (filebufsize); + + e = open_file (filename, bin ? FOPEN_BINARY_READ : "r"); + + tobuf = filebuf; + nread = 0; + while (1) + { + size_t got; + + got = fread (tobuf, 1, filebufsize - (tobuf - filebuf), e); + if (ferror (e)) + error (1, errno, "can't read %s", short_pathname); + nread += got; + tobuf += got; + + if (feof (e)) + break; + + /* It's probably paranoid to think S.ST_SIZE might be + too small to hold the entire file contents, but we + handle it just in case. */ + if (tobuf == filebuf + filebufsize) + { + int c; + long off; + + c = getc (e); + if (c == EOF) + break; + off = tobuf - filebuf; + expand_string (&filebuf, &filebufsize, filebufsize + 100); + tobuf = filebuf + off; + *tobuf++ = c; + ++nread; + } + } + + fclose (e); + + /* At this point the contents of the existing file are in + FILEBUF, and the length of the contents is in NREAD. + The contents of the patch from the network are in BUF, + and the length of the patch is in SIZE. */ + + if (! rcs_change_text (short_pathname, filebuf, nread, buf, size, + &patchedbuf, &patchedlen)) + patch_failed = 1; + else + { + if (stored_checksum_valid) + { + struct MD5Context context; + unsigned char checksum[16]; + + /* We have a checksum. Check it before writing + the file out, so that we don't have to read it + back in again. */ + MD5Init (&context); + MD5Update (&context, patchedbuf, patchedlen); + MD5Final (checksum, &context); + if (memcmp (checksum, stored_checksum, 16) != 0) + { + error (0, 0, + "checksum failure after patch to %s; will refetch", + short_pathname); + + patch_failed = 1; + } + + stored_checksum_valid = 0; + } - return; + if (! patch_failed) + { + e = open_file (temp_filename, + bin ? FOPEN_BINARY_WRITE : "w"); + if (fwrite (patchedbuf, 1, patchedlen, e) != patchedlen) + error (1, errno, "cannot write %s", temp_filename); + if (fclose (e) == EOF) + error (1, errno, "cannot close %s", temp_filename); + rename_file (temp_filename, filename); + } + + free (patchedbuf); } + + free (filebuf); } + free (temp_filename); - if (stored_checksum_valid) + if (stored_checksum_valid && ! patch_failed) { FILE *e; struct MD5Context context; @@ -1360,7 +1838,7 @@ update_entries (data_arg, ent_list, short_pathname, filename) * here using text mode, so its lines will be terminated the same * way they were transmitted. */ - e = fopen (filename, "r"); + e = CVS_FOPEN (filename, "r"); if (e == NULL) error (1, errno, "could not open %s", short_pathname); @@ -1385,19 +1863,27 @@ update_entries (data_arg, ent_list, short_pathname, filename) "checksum failure after patch to %s; will refetch", short_pathname); - /* Save this file to retrieve later. */ - failed_patches = - (char **) xrealloc ((char *) failed_patches, - ((failed_patches_count + 1) - * sizeof (char *))); - failed_patches[failed_patches_count] = - xstrdup (short_pathname); - ++failed_patches_count; - - return; + patch_failed = 1; } } + if (patch_failed) + { + /* Save this file to retrieve later. */ + failed_patches = (char **) xrealloc ((char *) failed_patches, + ((failed_patches_count + 1) + * sizeof (char *))); + failed_patches[failed_patches_count] = xstrdup (short_pathname); + ++failed_patches_count; + + stored_checksum_valid = 0; + + free (mode_string); + free (buf); + + return; + } + { /* FIXME: we should be respecting the umask. */ int status = change_mode (filename, mode_string); @@ -1422,6 +1908,8 @@ update_entries (data_arg, ent_list, short_pathname, filename) char *local_timestamp; char *file_timestamp; + (void) time (&last_register_time); + local_timestamp = data->timestamp; if (local_timestamp == NULL || ts[0] == '+') file_timestamp = time_stamp (filename); @@ -1459,6 +1947,7 @@ handle_checked_in (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_CHECKIN; + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; dat.timestamp = NULL; call_in_directory (args, update_entries, (char *)&dat); } @@ -1470,6 +1959,7 @@ handle_new_entry (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_CHECKIN; + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; dat.timestamp = "dummy timestamp from new-entry"; call_in_directory (args, update_entries, (char *)&dat); } @@ -1481,6 +1971,35 @@ handle_updated (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_UPDATE; + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; + dat.timestamp = NULL; + call_in_directory (args, update_entries, (char *)&dat); +} + +static void handle_created PROTO((char *, int)); + +static void +handle_created (args, len) + char *args; + int len; +{ + struct update_entries_data dat; + dat.contents = UPDATE_ENTRIES_UPDATE; + dat.existp = UPDATE_ENTRIES_NEW; + dat.timestamp = NULL; + call_in_directory (args, update_entries, (char *)&dat); +} + +static void handle_update_existing PROTO((char *, int)); + +static void +handle_update_existing (args, len) + char *args; + int len; +{ + struct update_entries_data dat; + dat.contents = UPDATE_ENTRIES_UPDATE; + dat.existp = UPDATE_ENTRIES_EXISTING; dat.timestamp = NULL; call_in_directory (args, update_entries, (char *)&dat); } @@ -1492,6 +2011,8 @@ handle_merged (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_UPDATE; + /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */ + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; dat.timestamp = "Result of merge"; call_in_directory (args, update_entries, (char *)&dat); } @@ -1503,6 +2024,21 @@ handle_patched (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_PATCH; + /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */ + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; + dat.timestamp = NULL; + call_in_directory (args, update_entries, (char *)&dat); +} + +static void +handle_rcs_diff (args, len) + char *args; + int len; +{ + struct update_entries_data dat; + dat.contents = UPDATE_ENTRIES_RCS_DIFF; + /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */ + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; dat.timestamp = NULL; call_in_directory (args, update_entries, (char *)&dat); } @@ -1533,6 +2069,11 @@ remove_entry_and_file (data, ent_list, short_pathname, filename) char *filename; { Scratch_Entry (ent_list, filename); + /* Note that we don't ignore existence_error's here. The server + should be sending Remove-entry rather than Removed in cases + where the file does not exist. And if the user removes the + file halfway through a cvs command, we should be printing an + error. */ if (unlink_file (filename) < 0) error (0, errno, "unable to remove %s", short_pathname); } @@ -1550,28 +2091,10 @@ static int is_cvsroot_level (pathname) char *pathname; { - char *short_pathname; - - if (strcmp (toplevel_repos, server_cvsroot) != 0) + if (strcmp (toplevel_repos, CVSroot_directory) != 0) return 0; - if (!use_directory) - { - if (strncmp (pathname, server_cvsroot, strlen (server_cvsroot)) != 0) - error (1, 0, - "server bug: pathname `%s' doesn't specify file in `%s'", - pathname, server_cvsroot); - short_pathname = pathname + strlen (server_cvsroot) + 1; - if (short_pathname[-1] != '/') - error (1, 0, - "server bug: pathname `%s' doesn't specify file in `%s'", - pathname, server_cvsroot); - return strchr (short_pathname, '/') == NULL; - } - else - { - return strchr (pathname, '/') == NULL; - } + return strchr (pathname, '/') == NULL; } static void @@ -1595,7 +2118,7 @@ handle_set_static_directory (args, len) if (strcmp (command_name, "export") == 0) { /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); return; } call_in_directory (args, set_static, (char *)NULL); @@ -1620,7 +2143,7 @@ handle_clear_static_directory (pathname, len) if (strcmp (command_name, "export") == 0) { /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); return; } @@ -1645,7 +2168,7 @@ set_sticky (data, ent_list, short_pathname, filename) char *tagspec; FILE *f; - read_line (&tagspec, 0); + read_line (&tagspec); f = open_file (CVSADM_TAG, "w+"); if (fprintf (f, "%s\n", tagspec) < 0) error (1, errno, "writing %s", CVSADM_TAG); @@ -1662,9 +2185,9 @@ handle_set_sticky (pathname, len) if (strcmp (command_name, "export") == 0) { /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); /* Swallow the tag line. */ - (void) read_line (NULL, 0); + read_line (NULL); return; } if (is_cvsroot_level (pathname)) @@ -1675,9 +2198,9 @@ handle_set_sticky (pathname, len) */ /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); /* Swallow the tag line. */ - (void) read_line (NULL, 0); + read_line (NULL); return; } @@ -1703,7 +2226,7 @@ handle_clear_sticky (pathname, len) if (strcmp (command_name, "export") == 0) { /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); return; } @@ -1766,7 +2289,7 @@ handle_set_checkin_prog (args, len) { char *prog; struct save_prog *p; - read_line (&prog, 0); + read_line (&prog); p = (struct save_prog *) xmalloc (sizeof (struct save_prog)); p->next = checkin_progs; p->dir = xstrdup (args); @@ -1781,7 +2304,7 @@ handle_set_update_prog (args, len) { char *prog; struct save_prog *p; - read_line (&prog, 0); + read_line (&prog); p = (struct save_prog *) xmalloc (sizeof (struct save_prog)); p->next = update_progs; p->dir = xstrdup (args); @@ -1797,15 +2320,17 @@ do_deferred_progs () struct save_prog *p; struct save_prog *q; - char fname[PATH_MAX]; + char *fname; FILE *f; - if (toplevel_wd[0] != '\0') - { - if (chdir (toplevel_wd) < 0) - error (1, errno, "could not chdir to %s", toplevel_wd); - } + + if (toplevel_wd != NULL) + { + if (CVS_CHDIR (toplevel_wd) < 0) + error (1, errno, "could not chdir to %s", toplevel_wd); + } for (p = checkin_progs; p != NULL; ) { + fname = xmalloc (strlen (p->dir) + sizeof CVSADM_CIPROG + 10); sprintf (fname, "%s/%s", p->dir, CVSADM_CIPROG); f = open_file (fname, "w"); if (fprintf (f, "%s\n", p->name) < 0) @@ -1817,10 +2342,12 @@ do_deferred_progs () q = p->next; free (p); p = q; + free (fname); } checkin_progs = NULL; - for (p = update_progs; p != NULL; p = p->next) + for (p = update_progs; p != NULL; ) { + fname = xmalloc (strlen (p->dir) + sizeof CVSADM_UPROG + 10); sprintf (fname, "%s/%s", p->dir, CVSADM_UPROG); f = open_file (fname, "w"); if (fprintf (f, "%s\n", p->name) < 0) @@ -1829,50 +2356,14 @@ do_deferred_progs () error (1, errno, "closing %s", fname); free (p->name); free (p->dir); + q = p->next; free (p); + p = q; + free (fname); } update_progs = NULL; } -static int client_isemptydir PROTO((char *)); - -/* - * Returns 1 if the argument directory exists and is completely empty, - * other than the existence of the CVS directory entry. Zero otherwise. - */ -static int -client_isemptydir (dir) - char *dir; -{ - DIR *dirp; - struct dirent *dp; - - if ((dirp = opendir (dir)) == NULL) - { - if (! existence_error (errno)) - error (0, errno, "cannot open directory %s for empty check", dir); - return (0); - } - errno = 0; - while ((dp = readdir (dirp)) != NULL) - { - if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 && - strcmp (dp->d_name, CVSADM) != 0) - { - (void) closedir (dirp); - return (0); - } - } - if (errno != 0) - { - error (0, errno, "cannot read directory %s", dir); - (void) closedir (dirp); - return (0); - } - (void) closedir (dirp); - return (1); -} - struct save_dir { char *dir; struct save_dir *next; @@ -1886,7 +2377,9 @@ add_prune_candidate (dir) { struct save_dir *p; - if (dir[0] == '.' && dir[1] == '\0') + if ((dir[0] == '.' && dir[1] == '\0') + || (prune_candidates != NULL + && strcmp (dir, prune_candidates->dir) == 0)) return; p = (struct save_dir *) xmalloc (sizeof (struct save_dir)); p->dir = xstrdup (dir); @@ -1902,22 +2395,34 @@ process_prune_candidates () struct save_dir *p; struct save_dir *q; - if (toplevel_wd[0] != '\0') - { - if (chdir (toplevel_wd) < 0) - error (1, errno, "could not chdir to %s", toplevel_wd); - } + if (toplevel_wd != NULL) + { + if (CVS_CHDIR (toplevel_wd) < 0) + error (1, errno, "could not chdir to %s", toplevel_wd); + } for (p = prune_candidates; p != NULL; ) { - if (client_isemptydir (p->dir)) + if (isemptydir (p->dir, 1)) { - unlink_file_dir (p->dir); + char *b; + + if (unlink_file_dir (p->dir) < 0) + error (0, errno, "cannot remove %s", p->dir); + b = strrchr (p->dir, '/'); + if (b == NULL) + Subdir_Deregister ((List *) NULL, (char *) NULL, p->dir); + else + { + *b = '\0'; + Subdir_Deregister ((List *) NULL, p->dir, b + 1); + } } free (p->dir); q = p->next; free (p); p = q; } + prune_candidates = NULL; } /* Send a Repository line. */ @@ -1972,23 +2477,33 @@ send_repository (dir, repos, update_dir) /* 80 is large enough for any of CVSADM_*. */ adm_name = xmalloc (strlen (dir) + 80); - if (use_directory == -1) - use_directory = supported_request ("Directory"); - - if (use_directory) - { - send_to_server ("Directory ", 0); - send_to_server (update_dir, 0); - send_to_server ("\012", 1); - send_to_server (repos, 0); - send_to_server ("\012", 1); - } - else + send_to_server ("Directory ", 0); { - send_to_server ("Repository ", 0); - send_to_server (repos, 0); - send_to_server ("\012", 1); + /* Send the directory name. I know that this + sort of duplicates code elsewhere, but each + case seems slightly different... */ + char buf[1]; + char *p = update_dir; + while (*p != '\0') + { + assert (*p != '\012'); + if (ISDIRSEP (*p)) + { + buf[0] = '/'; + send_to_server (buf, 1); + } + else + { + buf[0] = *p; + send_to_server (buf, 1); + } + ++p; + } } + send_to_server ("\012", 1); + send_to_server (repos, 0); + send_to_server ("\012", 1); + if (supported_request ("Static-directory")) { adm_name[0] = '\0'; @@ -2011,7 +2526,7 @@ send_repository (dir, repos, update_dir) else sprintf (adm_name, "%s/%s", dir, CVSADM_TAG); - f = fopen (adm_name, "r"); + f = CVS_FOPEN (adm_name, "r"); if (f == NULL) { if (! existence_error (errno)) @@ -2020,7 +2535,7 @@ send_repository (dir, repos, update_dir) else { char line[80]; - char *nl; + char *nl = NULL; send_to_server ("Sticky ", 0); while (fgets (line, sizeof (line), f) != NULL) { @@ -2043,7 +2558,7 @@ send_repository (dir, repos, update_dir) else sprintf (adm_name, "%s/%s", dir, CVSADM_CIPROG); - f = fopen (adm_name, "r"); + f = CVS_FOPEN (adm_name, "r"); if (f == NULL) { if (! existence_error (errno)) @@ -2052,7 +2567,7 @@ send_repository (dir, repos, update_dir) else { char line[80]; - char *nl; + char *nl = NULL; send_to_server ("Checkin-prog ", 0); @@ -2078,7 +2593,7 @@ send_repository (dir, repos, update_dir) else sprintf (adm_name, "%s/%s", dir, CVSADM_UPROG); - f = fopen (adm_name, "r"); + f = CVS_FOPEN (adm_name, "r"); if (f == NULL) { if (! existence_error (errno)) @@ -2087,7 +2602,7 @@ send_repository (dir, repos, update_dir) else { char line[80]; - char *nl; + char *nl = NULL; send_to_server ("Update-prog ", 0); @@ -2243,7 +2758,7 @@ client_expand_modules (argc, argv, local) for (i = 0; i < argc; ++i) send_arg (argv[i]); - send_a_repository ("", server_cvsroot, ""); + send_a_repository ("", CVSroot_directory, ""); send_to_server ("expand-modules\012", 0); @@ -2259,8 +2774,10 @@ client_expand_modules (argc, argv, local) } void -client_send_expansions (local) - int local; +client_send_expansions (local, where, build_dirs) + int local; + char *where; + int build_dirs; { int i; char *argv[1]; @@ -2275,17 +2792,17 @@ client_send_expansions (local) for (i = 0; i < modules_count; ++i) { - argv[0] = modules_vector[i]; + argv[0] = where ? where : modules_vector[i]; if (isfile (argv[0])) - send_files (1, argv, local, 0); + send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0); } - send_a_repository ("", server_cvsroot, ""); + send_a_repository ("", CVSroot_directory, ""); } void client_nonexpanded_setup () { - send_a_repository ("", server_cvsroot, ""); + send_a_repository ("", CVSroot_directory, ""); } static void @@ -2293,8 +2810,13 @@ handle_m (args, len) char *args; int len; { - fwrite (args, len, sizeof (*args), stdout); - putc ('\n', stdout); + /* In the case where stdout and stderr point to the same place, + fflushing stderr will make output happen in the correct order. + Often stderr will be line-buffered and this won't be needed, + but not always. */ + fflush (stderr); + fwrite (args, len, sizeof (*args), stdout); + putc ('\n', stdout); } static void @@ -2302,8 +2824,20 @@ handle_e (args, len) char *args; int len; { - fwrite (args, len, sizeof (*args), stderr); - putc ('\n', stderr); + /* In the case where stdout and stderr point to the same place, + fflushing stdout will make output happen in the correct order. */ + fflush (stdout); + fwrite (args, len, sizeof (*args), stderr); + putc ('\n', stderr); +} + +/*ARGSUSED*/ +static void +handle_f (args, len) + char *args; + int len; +{ + fflush (stderr); } #endif /* CLIENT_SUPPORT */ @@ -2328,8 +2862,12 @@ struct response responses[] = RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional), RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional), RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential), + RSP_LINE("Created", handle_created, response_type_normal, rs_optional), + RSP_LINE("Update-existing", handle_update_existing, response_type_normal, + rs_optional), RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential), RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional), + RSP_LINE("Rcs-diff", handle_rcs_diff, response_type_normal, rs_optional), RSP_LINE("Mode", handle_mode, response_type_normal, rs_optional), RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential), RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal, @@ -2355,6 +2893,7 @@ struct response responses[] = rs_optional), RSP_LINE("M", handle_m, response_type_normal, rs_essential), RSP_LINE("E", handle_e, response_type_normal, rs_essential), + RSP_LINE("F", handle_f, response_type_normal, rs_optional), /* Possibly should be response_type_error. */ RSP_LINE(NULL, NULL, response_type_normal, rs_essential) @@ -2375,91 +2914,52 @@ send_to_server (str, len) char *str; size_t len; { - if (len == 0) - len = strlen (str); - -#ifdef NO_SOCKET_TO_FD - if (use_socket_style) - { - int just_wrtn = 0; - size_t wrtn = 0; - -#ifdef VMS - /* send() blocks under VMS */ - if (send (server_sock, str + wrtn, len - wrtn, 0) < 0) - error (1, errno, "writing to server socket"); -#else /* VMS */ - while (wrtn < len) - { - just_wrtn = send (server_sock, str + wrtn, len - wrtn, 0); + static int nbytes; - if (just_wrtn == -1) - error (1, errno, "writing to server socket"); - - wrtn += just_wrtn; - if (wrtn == len) - break; - } -#endif /* VMS */ - } - else -#endif /* NO_SOCKET_TO_FD */ - { - size_t wrtn = 0; + if (len == 0) + len = strlen (str); + + buf_output (to_server, str, len); - while (wrtn < len) - { - wrtn += fwrite (str + wrtn, 1, len - wrtn, to_server); - - if (wrtn == len) - break; - - if (ferror (to_server)) - error (1, errno, "writing to server"); - if (feof (to_server)) - error (1, 0, "premature end-of-file on server"); - } + /* There is no reason not to send data to the server, so do it + whenever we've accumulated enough information in the buffer to + make it worth sending. */ + nbytes += len; + if (nbytes >= 2 * BUFFER_DATA_SIZE) + { + int status; + + status = buf_send_output (to_server); + if (status != 0) + error (1, status, "error writing to server"); + nbytes = 0; } - - if (to_server_logfile) - if (fwrite (str, 1, len, to_server_logfile) < len) - error (0, errno, "writing to to-server logfile"); } -/* Read up to LEN bytes from the server. Returns actual number of bytes - read. Gives a fatal error on EOF or error. */ +/* Read up to LEN bytes from the server. Returns actual number of + bytes read, which will always be at least one; blocks if there is + no data available at all. Gives a fatal error on EOF or error. */ static size_t try_read_from_server (buf, len) char *buf; size_t len; { - int nread; + int status, nread; + char *data; -#ifdef NO_SOCKET_TO_FD - if (use_socket_style) + status = buf_read_data (from_server, len, &data, &nread); + if (status != 0) { - nread = recv (server_sock, buf, len, 0); - if (nread == -1) - error (1, errno, "reading from server"); - } - else -#endif - { - nread = fread (buf, 1, len, from_server); - if (ferror (from_server)) - error (1, errno, "reading from server"); - if (feof (from_server)) + if (status == -1) error (1, 0, "end of file from server (consult above messages if any)"); + else if (status == -2) + error (1, 0, "out of memory"); + else + error (1, status, "reading from server"); } - /* Log, if that's what we're doing. */ - if (from_server_logfile != NULL && nread > 0) - { - size_t towrite = nread; - if (fwrite (buf, 1, towrite, from_server_logfile) < towrite) - error (0, errno, "writing to from-server logfile"); - } + memcpy (buf, data, nread); return nread; } @@ -2493,7 +2993,7 @@ get_server_responses () char *cmd; int len; - len = read_line (&cmd, 0); + len = read_line (&cmd); for (rs = responses; rs->name != NULL; ++rs) if (strncmp (cmd, rs->name, strlen (rs->name)) == 0) { @@ -2539,120 +3039,160 @@ int get_responses_and_close () { int errs = get_server_responses (); + int status; + + if (last_entries != NULL) + { + Entries_Close (last_entries); + last_entries = NULL; + } do_deferred_progs (); if (client_prune_dirs) process_prune_candidates (); + /* The calls to buf_shutdown are currently only meaningful when we + are using compression. First we shut down TO_SERVER. That + tells the server that its input is finished. It then shuts + down the buffer it is sending to us, at which point our shut + down of FROM_SERVER will complete. */ + + status = buf_shutdown (to_server); + if (status != 0) + error (0, status, "shutting down buffer to server"); + status = buf_shutdown (from_server); + if (status != 0) + error (0, status, "shutting down buffer from server"); + #ifdef NO_SOCKET_TO_FD if (use_socket_style) - { - if (shutdown (server_sock, 2) < 0) - error (1, errno, "shutting down server socket"); - } + { + if (shutdown (server_sock, 2) < 0) + error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); + } else #endif /* NO_SOCKET_TO_FD */ - { -#if defined(HAVE_KERBEROS) || defined(USE_DIRECT_TCP) || defined(AUTH_CLIENT_SUPPORT) - if (server_fd != -1) - { - if (shutdown (server_fd, 1) < 0) - error (1, errno, "shutting down connection to %s", server_host); + { +#if defined(HAVE_KERBEROS) || defined(AUTH_CLIENT_SUPPORT) + if (server_fd != -1) + { + if (shutdown (server_fd, 1) < 0) + error (1, 0, "shutting down connection to %s: %s", + CVSroot_hostname, SOCK_STRERROR (SOCK_ERRNO)); /* * This test will always be true because we dup the descriptor */ - if (fileno (from_server) != fileno (to_server)) - { - if (fclose (to_server) != 0) - error (1, errno, - "closing down connection to %s", - server_host); - } - } + if (fileno (from_server_fp) != fileno (to_server_fp)) + { + if (fclose (to_server_fp) != 0) + error (1, errno, + "closing down connection to %s", + CVSroot_hostname); + } + } else -#endif /* HAVE_KERBEROS || USE_DIRECT_TCP || AUTH_CLIENT_SUPPORT */ +#endif #ifdef SHUTDOWN_SERVER - SHUTDOWN_SERVER (fileno (to_server)); + SHUTDOWN_SERVER (fileno (to_server_fp)); #else /* ! SHUTDOWN_SERVER */ - { - + { + #ifdef START_RSH_WITH_POPEN_RW - if (pclose (to_server) == EOF) + if (pclose (to_server_fp) == EOF) #else /* ! START_RSH_WITH_POPEN_RW */ - if (fclose (to_server) == EOF) + if (fclose (to_server_fp) == EOF) #endif /* START_RSH_WITH_POPEN_RW */ - { - error (1, errno, "closing connection to %s", server_host); - } + { + error (1, errno, "closing connection to %s", + CVSroot_hostname); + } } - if (getc (from_server) != EOF) - error (0, 0, "dying gasps from %s unexpected", server_host); - else if (ferror (from_server)) - error (0, errno, "reading from %s", server_host); - - fclose (from_server); + if (! buf_empty_p (from_server) + || getc (from_server_fp) != EOF) + error (0, 0, "dying gasps from %s unexpected", CVSroot_hostname); + else if (ferror (from_server_fp)) + error (0, errno, "reading from %s", CVSroot_hostname); + + fclose (from_server_fp); #endif /* SHUTDOWN_SERVER */ - } - -#if ! RSH_NOT_TRANSPARENT + } + if (rsh_pid != -1 && waitpid (rsh_pid, (int *) 0, 0) == -1) error (1, errno, "waiting for process %d", rsh_pid); -#endif /* ! RSH_NOT_TRANSPARENT */ server_started = 0; + /* see if we need to sleep before returning */ + if (last_register_time) + { + time_t now; + + (void) time (&now); + if (now == last_register_time) + sleep (1); /* to avoid time-stamp races */ + } + return errs; } -#ifndef RSH_NOT_TRANSPARENT static void start_rsh_server PROTO((int *, int *)); -#endif /* RSH_NOT_TRANSPARENT */ int supported_request (name) - char *name; + char *name; { - struct request *rq; - - for (rq = requests; rq->name; rq++) - if (!strcmp (rq->name, name)) - return rq->status == rq_supported; - error (1, 0, "internal error: testing support for unknown option?"); - /* NOTREACHED */ - return 0; + struct request *rq; + + for (rq = requests; rq->name; rq++) + if (!strcmp (rq->name, name)) + return rq->status == rq_supported; + error (1, 0, "internal error: testing support for unknown option?"); + /* NOTREACHED */ + return 0; } #ifdef AUTH_CLIENT_SUPPORT -void +static void init_sockaddr PROTO ((struct sockaddr_in *, char *, + unsigned int)); + +static void init_sockaddr (name, hostname, port) struct sockaddr_in *name; - const char *hostname; - unsigned short int port; + char *hostname; + unsigned int port; { struct hostent *hostinfo; + unsigned short shortport = port; memset (name, 0, sizeof (*name)); name->sin_family = AF_INET; - name->sin_port = htons (port); + name->sin_port = htons (shortport); hostinfo = gethostbyname (hostname); if (hostinfo == NULL) { fprintf (stderr, "Unknown host %s.\n", hostname); - exit (EXIT_FAILURE); + error_exit (); } name->sin_addr = *(struct in_addr *) hostinfo->h_addr; } -int +static int auth_server_port_number PROTO ((void)); + +static int auth_server_port_number () { - return CVS_AUTH_PORT; + struct servent *s = getservbyname ("cvspserver", "tcp"); + + if (s) + return ntohs (s->s_port); + else + return CVS_AUTH_PORT; } @@ -2679,29 +3219,34 @@ connect_to_pserver (tofdp, fromfdp, verify_only) int port_number; struct sockaddr_in client_sai; - /* Does nothing if already called before now. */ - parse_cvsroot (); - sock = socket (AF_INET, SOCK_STREAM, 0); if (sock == -1) { fprintf (stderr, "socket() failed\n"); - exit (EXIT_FAILURE); + error_exit (); } port_number = auth_server_port_number (); - init_sockaddr (&client_sai, server_host, port_number); + init_sockaddr (&client_sai, CVSroot_hostname, port_number); if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai)) < 0) - error (1, errno, "connect to %s:%d failed", server_host, - CVS_AUTH_PORT); + error (1, 0, "connect to %s:%d failed: %s", CVSroot_hostname, + port_number, SOCK_STRERROR (SOCK_ERRNO)); /* Run the authorization mini-protocol before anything else. */ { int i; - char ch, read_buf[PATH_MAX]; + char ch; + + /* Long enough to hold I LOVE YOU or I HATE YOU. Using a fixed-size + buffer seems better than letting an apeshit server chew up our + memory with illegal responses, and the value comes from + the protocol itself; it is not an arbitrary limit on data sent. */ +#define LARGEST_RESPONSE 80 + char read_buf[LARGEST_RESPONSE]; + char *begin = NULL; - char *repository = server_cvsroot; - char *username = server_user; + char *repository = CVSroot_directory; + char *username = CVSroot_username; char *password = NULL; char *end = NULL; @@ -2717,7 +3262,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only) } /* Get the password, probably from ~/.cvspass. */ - password = get_cvs_password (server_user, server_host, server_cvsroot); + password = get_cvs_password (); /* Announce that we're starting the authorization protocol. */ send (sock, begin, strlen (begin), 0); @@ -2747,11 +3292,12 @@ connect_to_pserver (tofdp, fromfdp, verify_only) * end of both ACK and NACK, and we loop, reading until "\n". */ ch = 0; - memset (read_buf, 0, PATH_MAX); - for (i = 0; (i < (PATH_MAX - 1)) && (ch != '\n'); i++) + memset (read_buf, 0, LARGEST_RESPONSE); + for (i = 0; (i < (LARGEST_RESPONSE - 1)) && (ch != '\n'); i++) { if (recv (sock, &ch, 1, 0) < 0) - error (1, errno, "recv() from server %s", server_host); + error (1, 0, "recv() from server %s: %s", CVSroot_hostname, + SOCK_STRERROR (SOCK_ERRNO)); read_buf[i] = ch; } @@ -2763,9 +3309,10 @@ connect_to_pserver (tofdp, fromfdp, verify_only) { error (0, 0, "authorization failed: server %s rejected access", - server_host); - error (1, errno, - "shutdown() failed (server %s)", server_host); + CVSroot_hostname); + error (1, 0, + "shutdown() failed (server %s): %s", CVSroot_hostname, + SOCK_STRERROR (SOCK_ERRNO)); } if (verify_only) @@ -2773,7 +3320,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only) else error (1, 0, "authorization failed: server %s rejected access", - server_host); + CVSroot_hostname); } else if (strcmp (read_buf, "I LOVE YOU\n") != 0) { @@ -2782,19 +3329,22 @@ connect_to_pserver (tofdp, fromfdp, verify_only) { error (0, 0, "unrecognized auth response from %s: %s", - server_host, read_buf); - error (1, errno, "shutdown() failed, server %s", server_host); + CVSroot_hostname, read_buf); + error (1, 0, + "shutdown() failed, server %s: %s", CVSroot_hostname, + SOCK_STRERROR (SOCK_ERRNO)); } error (1, 0, "unrecognized auth response from %s: %s", - server_host, read_buf); + CVSroot_hostname, read_buf); } } if (verify_only) { if (shutdown (sock, 2) < 0) - error (0, errno, "shutdown() failed, server %s", server_host); + error (0, 0, "shutdown() failed, server %s: %s", CVSroot_hostname, + SOCK_STRERROR (SOCK_ERRNO)); return 1; } else @@ -2820,158 +3370,154 @@ connect_to_pserver (tofdp, fromfdp, verify_only) #endif /* AUTH_CLIENT_SUPPORT */ -#if HAVE_KERBEROS || USE_DIRECT_TCP +#if HAVE_KERBEROS /* * FIXME: this function has not been changed to deal with * NO_SOCKET_TO_FD (i.e., systems on which sockets cannot be converted - * to file descriptors. The first person to try building a kerberos - * client on such a system (OS/2, Windows 95, and maybe others) will - * have to make take care of this. + * to file descriptors) or with SOCK_ERRNO/SOCK_STRERROR. The first + * person to try building a kerberos client on such a system (OS/2, + * Windows 95, and maybe others) will have to make take care of this. */ void start_tcp_server (tofdp, fromfdp) - int *tofdp, *fromfdp; + int *tofdp, *fromfdp; { - int tofd, fromfd; - - struct hostent *hp; - char *hname; - const char *portenv; - int port; - struct sockaddr_in sin; - int s; + int tofd = -1, fromfd; + struct hostent *hp; + char *hname; + const char *portenv; + int port; + struct sockaddr_in sin; + int s; #if HAVE_KERBEROS - KTEXT_ST ticket; - const char *realm; + KTEXT_ST ticket; + const char *realm; #endif /* HAVE_KERBEROS */ - int status; - - /* - * We look up the host to give a better error message if it - * does not exist. However, we then pass server_host to - * krb_sendauth, rather than the canonical name, because - * krb_sendauth is going to do its own canonicalization anyhow - * and that lets us not worry about the static storage used by - * gethostbyname. - */ - hp = gethostbyname (server_host); - if (hp == NULL) - error (1, 0, "%s: unknown host", server_host); - hname = xmalloc (strlen (hp->h_name) + 1); - strcpy (hname, hp->h_name); + int status; + + /* + * We look up the host to give a better error message if it + * does not exist. However, we then pass CVSroot_hostname to + * krb_sendauth, rather than the canonical name, because + * krb_sendauth is going to do its own canonicalization anyhow + * and that lets us not worry about the static storage used by + * gethostbyname. + */ + hp = gethostbyname (CVSroot_hostname); + if (hp == NULL) + error (1, 0, "%s: unknown host", CVSroot_hostname); + hname = xmalloc (strlen (hp->h_name) + 1); + strcpy (hname, hp->h_name); #if HAVE_KERBEROS - realm = krb_realmofhost (hname); + realm = krb_realmofhost (hname); #endif /* HAVE_KERBEROS */ - - /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */ - portenv = getenv ("CVS_CLIENT_PORT"); - if (portenv != NULL) + + /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */ + portenv = getenv ("CVS_CLIENT_PORT"); + if (portenv != NULL) { - port = atoi (portenv); - if (port <= 0) - goto try_rsh_no_message; - if (trace) - fprintf(stderr, "Using TCP port %d to contact server.\n", port); - port = htons (port); + port = atoi (portenv); + if (port <= 0) + { + error (0, 0, "CVS_CLIENT_PORT must be a positive number! If you"); + error (0, 0, "are trying to force a connection via rsh, please"); + error (0, 0, "put \":server:\" at the beginning of your CVSROOT"); + error (1, 0, "variable."); + } + if (trace) + fprintf(stderr, "Using TCP port %d to contact server.\n", port); + port = htons (port); } - else + else { - struct servent *sp; - - sp = getservbyname ("cvs", "tcp"); - if (sp == NULL) - port = htons (CVS_PORT); - else - port = sp->s_port; + struct servent *sp; + + sp = getservbyname ("cvs", "tcp"); + if (sp == NULL) + port = htons (CVS_PORT); + else + port = sp->s_port; } - - s = socket (AF_INET, SOCK_STREAM, 0); - if (s < 0) - error (1, errno, "socket"); - - memset (&sin, 0, sizeof sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - sin.sin_port = 0; - - if (bind (s, (struct sockaddr *) &sin, sizeof sin) < 0) - error (1, errno, "bind"); - - memcpy (&sin.sin_addr, hp->h_addr, hp->h_length); - sin.sin_port = port; - - tofd = -1; - if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0) + + s = socket (AF_INET, SOCK_STREAM, 0); + if (s < 0) + error (1, errno, "socket"); + + memset (&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = 0; + + if (bind (s, (struct sockaddr *) &sin, sizeof sin) < 0) + error (1, errno, "bind"); + + memcpy (&sin.sin_addr, hp->h_addr, hp->h_length); + sin.sin_port = port; + + if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0) { - error (0, errno, "connect"); - close (s); + error (0, errno, "connect"); + close (s); } - else + else { #ifdef HAVE_KERBEROS - struct sockaddr_in laddr; - int laddrlen; - MSG_DAT msg_data; - CREDENTIALS cred; - Key_schedule sched; - - laddrlen = sizeof (laddr); - if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0) - error (1, errno, "getsockname"); - - /* We don't care about the checksum, and pass it as zero. */ - status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd", - hname, realm, (unsigned long) 0, &msg_data, - &cred, sched, &laddr, &sin, "KCVSV1.0"); - if (status != KSUCCESS) + struct sockaddr_in laddr; + int laddrlen; + MSG_DAT msg_data; + CREDENTIALS cred; + + laddrlen = sizeof (laddr); + if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0) + error (1, errno, "getsockname"); + + /* We don't care about the checksum, and pass it as zero. */ + status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd", + hname, realm, (unsigned long) 0, &msg_data, + &cred, sched, &laddr, &sin, "KCVSV1.0"); + if (status != KSUCCESS) { - error (0, 0, "kerberos: %s", krb_get_err_text(status)); - close (s); + error (0, 0, "kerberos: %s", krb_get_err_text(status)); + close (s); } - else + else { + memcpy (kblock, cred.session, sizeof (C_Block)); + #endif /* HAVE_KERBEROS */ - server_fd = s; - close_on_exec (server_fd); - tofd = fromfd = s; + server_fd = s; + close_on_exec (server_fd); + tofd = fromfd = s; #ifdef HAVE_KERBEROS } #endif /* HAVE_KERBEROS */ } - if (tofd == -1) + if (tofd == -1) { - /* FIXME: Falling back like this is slow and we should probably - just make it a fatal error (so that people use the right - environment variables or, when we get around to implementing - the right ones, access methods). */ - error (0, 0, "trying to start server using rsh"); - try_rsh_no_message: - server_fd = -1; -#if ! RSH_NOT_TRANSPARENT - start_rsh_server (&tofd, &fromfd); -#else /* RSH_NOT_TRANSPARENT */ -#if defined (START_SERVER) - START_SERVER (&tofd, &fromfd, getcaller (), - server_user, server_host, server_cvsroot); -#endif /* defined (START_SERVER) */ -#endif /* ! RSH_NOT_TRANSPARENT */ +#ifdef HAVE_KERBEROS + error (0, 0, "Kerberos connect failed"); +#else + error (0, 0, "Direct TCP connect failed"); +#endif + error (1, 0, "couldn't connect to remote host %s", CVSroot_hostname); } - free (hname); - /* Give caller the values it wants. */ - *tofdp = tofd; - *fromfdp = fromfd; + free (hname); + + /* Give caller the values it wants. */ + *tofdp = tofd; + *fromfdp = fromfd; } -#endif /* HAVE_KERBEROS || USE_DIRECT_TCP */ +#endif /* HAVE_KERBEROS */ static int send_variable_proc PROTO ((Node *, void *)); @@ -2992,80 +3538,77 @@ send_variable_proc (node, closure) void start_server () { - int tofd, fromfd; - char *log = getenv ("CVS_CLIENT_LOG"); + int tofd, fromfd; + char *log = getenv ("CVS_CLIENT_LOG"); - /* Note that generally speaking we do *not* fall back to a different - way of connecting if the first one does not work. This is slow - (*really* slow on a 14.4kbps link); the clean way to have a CVS - which supports several ways of connecting is with access methods. */ + /* Note that generally speaking we do *not* fall back to a different + way of connecting if the first one does not work. This is slow + (*really* slow on a 14.4kbps link); the clean way to have a CVS + which supports several ways of connecting is with access methods. */ - /* Init these to NULL. They will be set later if logging is on. */ - from_server_logfile = (FILE *) NULL; - to_server_logfile = (FILE *) NULL; + switch (CVSroot_method) + { #ifdef AUTH_CLIENT_SUPPORT - if (use_authenticating_server) - { - /* Toss the return value. It will die with error if anything - goes wrong anyway. */ - connect_to_pserver (&tofd, &fromfd, 0); - } - else -#endif /* AUTH_CLIENT_SUPPORT */ - { -#if HAVE_KERBEROS || USE_DIRECT_TCP - start_tcp_server (&tofd, &fromfd); -#else + case pserver_method: + /* Toss the return value. It will die with error if anything + goes wrong anyway. */ + connect_to_pserver (&tofd, &fromfd, 0); + break; +#endif + +#if HAVE_KERBEROS + case kserver_method: + start_tcp_server (&tofd, &fromfd); + break; +#endif -# if ! RSH_NOT_TRANSPARENT - start_rsh_server (&tofd, &fromfd); -# else + case ext_method: + start_rsh_server (&tofd, &fromfd); + break; -# if defined(START_SERVER) - START_SERVER (&tofd, &fromfd, getcaller (), - server_user, server_host, server_cvsroot); -# endif + case server_method: +#if defined(START_SERVER) + START_SERVER (&tofd, &fromfd, getcaller (), + CVSroot_username, CVSroot_hostname, + CVSroot_directory); +# if defined (START_SERVER_RETURNS_SOCKET) && defined (NO_SOCKET_TO_FD) + /* This is a system on which we can only write to a socket + using send/recv. Therefore its START_SERVER needs to + return a socket. */ + use_socket_style = 1; + server_sock = tofd; # endif + +#else + /* FIXME: It should be possible to implement this portably, + like pserver, which would get rid of the duplicated code + in {vms,windows-NT,...}/startserver.c. */ + error (1, 0, "\ +the :server: access method is not supported by this port of CVS"); #endif - } + break; -#if defined(VMS) && defined(NO_SOCKET_TO_FD) - /* Avoid mixing sockets with stdio */ - use_socket_style = 1; - server_sock = tofd; -#endif /* VMS && NO_SOCKET_TO_FD */ + default: + error (1, 0, "\ +(start_server internal error): unknown access method"); + break; + } /* "Hi, I'm Darlene and I'll be your server tonight..." */ server_started = 1; - /* Set up logfiles, if any. */ - if (log) +#ifdef NO_SOCKET_TO_FD + if (use_socket_style) { - int len = strlen (log); - char *buf = xmalloc (len + 5); - char *p; - - strcpy (buf, log); - p = buf + len; - - strcpy (p, ".in"); - to_server_logfile = open_file (buf, "w"); - if (to_server_logfile == NULL) - error (0, errno, "opening to-server logfile %s", buf); - - strcpy (p, ".out"); - from_server_logfile = open_file (buf, "w"); - if (from_server_logfile == NULL) - error (0, errno, "opening from-server logfile %s", buf); - - free (buf); + to_server = socket_buffer_initialize (server_sock, 0, + buf_memory_error); + from_server = socket_buffer_initialize (server_sock, 1, + buf_memory_error); } - -#ifdef NO_SOCKET_TO_FD - if (! use_socket_style) + else #endif /* NO_SOCKET_TO_FD */ - { + { /* todo: some OS's don't need these calls... */ close_on_exec (tofd); close_on_exec (fromfd); @@ -3081,26 +3624,65 @@ start_server () } /* These will use binary mode on systems which have it. */ - to_server = fdopen (tofd, FOPEN_BINARY_WRITE); - if (to_server == NULL) - error (1, errno, "cannot fdopen %d for write", tofd); - from_server = fdopen (fromfd, FOPEN_BINARY_READ); - if (from_server == NULL) - error (1, errno, "cannot fdopen %d for read", fromfd); - } + to_server_fp = fdopen (tofd, FOPEN_BINARY_WRITE); + if (to_server_fp == NULL) + error (1, errno, "cannot fdopen %d for write", tofd); + to_server = stdio_buffer_initialize (to_server_fp, 0, + buf_memory_error); + + from_server_fp = fdopen (fromfd, FOPEN_BINARY_READ); + if (from_server_fp == NULL) + error (1, errno, "cannot fdopen %d for read", fromfd); + from_server = stdio_buffer_initialize (from_server_fp, 1, + buf_memory_error); + } + + /* Set up logfiles, if any. */ + if (log) + { + int len = strlen (log); + char *buf = xmalloc (len + 5); + char *p; + FILE *fp; + + strcpy (buf, log); + p = buf + len; + + /* Open logfiles in binary mode so that they reflect + exactly what was transmitted and received (that is + more important than that they be maximally + convenient to view). */ + strcpy (p, ".in"); + fp = open_file (buf, "wb"); + if (fp == NULL) + error (0, errno, "opening to-server logfile %s", buf); + else + to_server = log_buffer_initialize (to_server, fp, 0, + buf_memory_error); + + strcpy (p, ".out"); + fp = open_file (buf, "wb"); + if (fp == NULL) + error (0, errno, "opening from-server logfile %s", buf); + else + from_server = log_buffer_initialize (from_server, fp, 1, + buf_memory_error); + + free (buf); + } /* Clear static variables. */ if (toplevel_repos != NULL) - free (toplevel_repos); + free (toplevel_repos); toplevel_repos = NULL; if (last_dir_name != NULL) - free (last_dir_name); + free (last_dir_name); last_dir_name = NULL; if (last_repos != NULL) - free (last_repos); + free (last_repos); last_repos = NULL; if (last_update_dir != NULL) - free (last_update_dir); + free (last_update_dir); last_update_dir = NULL; stored_checksum_valid = 0; stored_mode_valid = 0; @@ -3108,7 +3690,7 @@ start_server () if (strcmp (command_name, "init") != 0) { send_to_server ("Root ", 0); - send_to_server (server_cvsroot, 0); + send_to_server (CVSroot_directory, 0); send_to_server ("\012", 1); } @@ -3127,7 +3709,7 @@ start_server () send_to_server ("valid-requests\012", 0); if (get_server_responses ()) - exit (EXIT_FAILURE); + error_exit (); /* * Now handle global options. @@ -3207,9 +3789,55 @@ start_server () "This server does not support the global -l option."); } } + if (cvsencrypt) + { +#ifdef ENCRYPTION + /* Turn on encryption before turning on compression. We do + not want to try to compress the encrypted stream. Instead, + we want to encrypt the compressed stream. If we can't turn + on encryption, bomb out; don't let the user think the data + is being encrypted when it is not. */ +#ifdef HAVE_KERBEROS + if (CVSroot_method == kserver_method) + { + if (! supported_request ("Kerberos-encrypt")) + error (1, 0, "This server does not support encryption"); + send_to_server ("Kerberos-encrypt\012", 0); + to_server = krb_encrypt_buffer_initialize (to_server, 0, sched, + kblock, + buf_memory_error); + from_server = krb_encrypt_buffer_initialize (from_server, 1, + sched, kblock, + buf_memory_error); + } + else +#endif /* HAVE_KERBEROS */ + error (1, 0, "Encryption is only supported when using Kerberos"); +#else /* ! ENCRYPTION */ + error (1, 0, "This client does not support encryption"); +#endif /* ! ENCRYPTION */ + } if (gzip_level) { - if (supported_request ("gzip-file-contents")) + if (supported_request ("Gzip-stream")) + { + char gzip_level_buf[5]; + send_to_server ("Gzip-stream ", 0); + sprintf (gzip_level_buf, "%d", gzip_level); + send_to_server (gzip_level_buf, 0); + send_to_server ("\012", 1); + + /* All further communication with the server will be + compressed. */ + + to_server = compress_buffer_initialize (to_server, 0, gzip_level, + buf_memory_error); + from_server = compress_buffer_initialize (from_server, 1, + gzip_level, + buf_memory_error); + } +#ifndef NO_CLIENT_GZIP_PROCESS + else if (supported_request ("gzip-file-contents")) { char gzip_level_buf[5]; send_to_server ("gzip-file-contents ", 0); @@ -3217,10 +3845,16 @@ start_server () send_to_server (gzip_level_buf, 0); send_to_server ("\012", 1); + + file_gzip_level = gzip_level; } +#endif else { fprintf (stderr, "server doesn't support gzip-file-contents\n"); + /* Setting gzip_level to 0 prevents us from giving the + error twice if update has to contact the server again + to fetch unpatchable files. */ gzip_level = 0; } } @@ -3239,7 +3873,6 @@ start_server () walklist (variable_list, send_variable_proc, NULL); } -#ifndef RSH_NOT_TRANSPARENT /* Contact the server by starting it with rsh. */ /* Right now, we have two different definitions for this function, @@ -3258,68 +3891,81 @@ start_server () static void start_rsh_server (tofdp, fromfdp) - int *tofdp, *fromfdp; + int *tofdp, *fromfdp; { - int pipes[2]; - - /* If you're working through firewalls, you can set the - CVS_RSH environment variable to a script which uses rsh to - invoke another rsh on a proxy machine. */ - char *cvs_rsh = getenv ("CVS_RSH"); - char *cvs_server = getenv ("CVS_SERVER"); - char command[PATH_MAX]; - int i = 0; - /* This needs to fit "rsh", "-b", "-l", "USER", "host", - "cmd (w/ args)", and NULL. We leave some room to grow. */ - char *rsh_argv[10]; - - if (!cvs_rsh) - cvs_rsh = "rsh"; - if (!cvs_server) - cvs_server = "cvs"; - - /* If you are running a very old (Nov 3, 1994, before 1.5) - * version of the server, you need to make sure that your .bashrc - * on the server machine does not set CVSROOT to something - * containing a colon (or better yet, upgrade the server). */ - - /* The command line starts out with rsh. */ - rsh_argv[i++] = cvs_rsh; - + int pipes[2]; + + /* If you're working through firewalls, you can set the + CVS_RSH environment variable to a script which uses rsh to + invoke another rsh on a proxy machine. */ + char *cvs_rsh = getenv ("CVS_RSH"); + char *cvs_server = getenv ("CVS_SERVER"); + int i = 0; + /* This needs to fit "rsh", "-b", "-l", "USER", "host", + "cmd (w/ args)", and NULL. We leave some room to grow. */ + char *rsh_argv[10]; + + if (!cvs_rsh) + /* People sometimes suggest or assume that this should default + to "remsh" on systems like HPUX in which that is the + system-supplied name for the rsh program. However, that + causes various problems (keep in mind that systems such as + HPUX might have non-system-supplied versions of "rsh", like + a Kerberized one, which one might want to use). If we + based the name on what is found in the PATH of the person + who runs configure, that would make it harder to + consistently produce the same result in the face of + different people producing binary distributions. If we + based it on "remsh" always being the default for HPUX + (e.g. based on uname), that might be slightly better but + would require us to keep track of what the defaults are for + each system type, and probably would cope poorly if the + existence of remsh or rsh varies from OS version to OS + version. Therefore, it seems best to have the default + remain "rsh", and tell HPUX users to specify remsh, for + example in CVS_RSH or other such mechanisms to be devised, + if that is what they want (the manual already tells them + that). */ + cvs_rsh = "rsh"; + if (!cvs_server) + cvs_server = "cvs"; + + /* The command line starts out with rsh. */ + rsh_argv[i++] = cvs_rsh; + #ifdef RSH_NEEDS_BINARY_FLAG - /* "-b" for binary, under OS/2. */ - rsh_argv[i++] = "-b"; + /* "-b" for binary, under OS/2. */ + rsh_argv[i++] = "-b"; #endif /* RSH_NEEDS_BINARY_FLAG */ - /* Then we strcat more things on the end one by one. */ - if (server_user != NULL) + /* Then we strcat more things on the end one by one. */ + if (CVSroot_username != NULL) { - rsh_argv[i++] = "-l"; - rsh_argv[i++] = server_user; + rsh_argv[i++] = "-l"; + rsh_argv[i++] = CVSroot_username; } - - rsh_argv[i++] = server_host; - rsh_argv[i++] = cvs_server; - rsh_argv[i++] = "server"; - /* Mark the end of the arg list. */ - rsh_argv[i] = (char *) NULL; + rsh_argv[i++] = CVSroot_hostname; + rsh_argv[i++] = cvs_server; + rsh_argv[i++] = "server"; + + /* Mark the end of the arg list. */ + rsh_argv[i] = (char *) NULL; - if (trace) + if (trace) { - fprintf (stderr, " -> Starting server: "); - fprintf (stderr, "%s", command); - putc ('\n', stderr); + fprintf (stderr, " -> Starting server: "); + putc ('\n', stderr); } - - /* Do the deed. */ - rsh_pid = popenRW (rsh_argv, pipes); - if (rsh_pid < 0) - error (1, errno, "cannot start server via rsh"); - - /* Give caller the file descriptors. */ - *tofdp = pipes[0]; - *fromfdp = pipes[1]; + + /* Do the deed. */ + rsh_pid = popenRW (rsh_argv, pipes); + if (rsh_pid < 0) + error (1, errno, "cannot start server via rsh"); + + /* Give caller the file descriptors. */ + *tofdp = pipes[0]; + *fromfdp = pipes[1]; } #else /* ! START_RSH_WITH_POPEN_RW */ @@ -3346,7 +3992,7 @@ start_rsh_server (tofdp, fromfdp) versions of rsh that grab switches out of the middle of the command (they're calling the GNU getopt routines incorrectly). */ command = xmalloc (strlen (cvs_server) - + strlen (server_cvsroot) + + strlen (CVSroot_directory) + 50); /* If you are running a very old (Nov 3, 1994, before 1.5) @@ -3360,15 +4006,15 @@ start_rsh_server (tofdp, fromfdp) char **p = argv; *p++ = cvs_rsh; - *p++ = server_host; + *p++ = CVSroot_hostname; /* If the login names differ between client and server * pass it on to rsh. */ - if (server_user != NULL) + if (CVSroot_username != NULL) { *p++ = "-l"; - *p++ = server_user; + *p++ = CVSroot_username; } *p++ = command; @@ -3388,10 +4034,10 @@ start_rsh_server (tofdp, fromfdp) if (rsh_pid < 0) error (1, errno, "cannot start server via rsh"); } + free (command); } #endif /* START_RSH_WITH_POPEN_RW */ -#endif /* ! RSH_NOT_TRANSPARENT */ @@ -3423,6 +4069,10 @@ send_arg (string) static void send_modified PROTO ((char *, char *, Vers_TS *)); +/* VERS->OPTIONS specifies whether the file is binary or not. NOTE: BEFORE + using any other fields of the struct vers, we would need to fix + client_process_import_file to set them up. */ + static void send_modified (file, short_pathname, vers) char *file; @@ -3437,8 +4087,11 @@ send_modified (file, short_pathname, vers) int bufsize; int bin; + if (trace) + (void) fprintf (stderr, " -> Sending file `%s' to server\n", file); + /* Don't think we can assume fstat exists. */ - if (stat (file, &sb) < 0) + if ( CVS_STAT (file, &sb) < 0) error (1, errno, "reading %s", short_pathname); mode_string = mode_to_string (sb.st_mode); @@ -3459,25 +4112,43 @@ send_modified (file, short_pathname, vers) else bin = 0; - fd = open (file, O_RDONLY | (bin ? OPEN_BINARY : 0)); +#ifdef BROKEN_READWRITE_CONVERSION + if (!bin) + { + /* If only stdio, not open/write/etc., do text/binary + conversion, use convert_file which can compensate + (FIXME: we could just use stdio instead which would + avoid the whole problem). */ + char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP"); + convert_file (file, O_RDONLY, + tfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY); + fd = CVS_OPEN (tfile, O_RDONLY | OPEN_BINARY); + if (fd < 0) + error (1, errno, "reading %s", short_pathname); + } + else + fd = CVS_OPEN (file, O_RDONLY | OPEN_BINARY); +#else + fd = CVS_OPEN (file, O_RDONLY | (bin ? OPEN_BINARY : 0)); +#endif if (fd < 0) error (1, errno, "reading %s", short_pathname); - if (gzip_level && sb.st_size > 100) + if (file_gzip_level && sb.st_size > 100) { int nread, newsize = 0, gzip_status; pid_t gzip_pid; char *bufp = buf; int readsize = 8192; #ifdef LINES_CRLF_TERMINATED - char tempfile[L_tmpnam]; + char *tempfile; int converting; #endif /* LINES_CRLF_TERMINATED */ #ifdef LINES_CRLF_TERMINATED - /* Assume everything in a "cvs import" is text. */ if (vers == NULL) + /* "Can't happen". */ converting = 1; else /* Otherwise, we convert things unless they're binary. */ @@ -3503,7 +4174,7 @@ send_modified (file, short_pathname, vers) if (close (fd) < 0) error (0, errno, "warning: can't close %s", short_pathname); - tmpnam (tempfile); + tempfile = cvs_temp_name (); convert_file (file, O_RDONLY, tempfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY); @@ -3513,13 +4184,13 @@ send_modified (file, short_pathname, vers) do remember something obscure in the manuals about propagating the translation mode to created processes via environment variables, ick. */ - fd = open (tempfile, O_RDONLY | OPEN_BINARY); + fd = CVS_OPEN (tempfile, O_RDONLY | OPEN_BINARY); if (fd < 0) error (1, errno, "reading %s", short_pathname); } #endif /* LINES_CRLF_TERMINATED */ - fd = filter_through_gzip (fd, 1, gzip_level, &gzip_pid); + fd = filter_through_gzip (fd, 1, file_gzip_level, &gzip_pid); /* FIXME: is there any reason to go through all this realloc'ing when we could just be writing the data to the network as we read @@ -3558,9 +4229,11 @@ send_modified (file, short_pathname, vers) #if LINES_CRLF_TERMINATED if (converting) { - if (unlink (tempfile) < 0) + if ( CVS_UNLINK (tempfile) < 0) error (0, errno, "warning: can't remove temp file %s", tempfile); + free (tempfile); + tempfile = NULL; } #endif /* LINES_CRLF_TERMINATED */ @@ -3613,48 +4286,80 @@ send_modified (file, short_pathname, vers) sprintf (tmp, "%lu\012", (unsigned long) newsize); send_to_server (tmp, 0); } +#ifdef BROKEN_READWRITE_CONVERSION + if (!bin) + { + char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP"); + if (CVS_UNLINK (tfile) < 0) + error (0, errno, "warning: can't remove temp file %s", tfile); + } +#endif /* * Note that this only ends with a newline if the file ended with * one. */ if (newsize > 0) - send_to_server (buf, newsize); + send_to_server (buf, newsize); } free (buf); free (mode_string); } -static int send_fileproc PROTO ((struct file_info *finfo)); +/* The address of an instance of this structure is passed to + send_fileproc, send_filesdoneproc, and send_direntproc, as the + callerdat parameter. */ + +struct send_data +{ + /* Each of the following flags are zero for clear or nonzero for set. */ + int build_dirs; + int force; + int no_contents; +}; + +static int send_fileproc PROTO ((void *callerdat, struct file_info *finfo)); /* Deal with one file. */ static int -send_fileproc (finfo) +send_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { + struct send_data *args = (struct send_data *) callerdat; Vers_TS *vers; + struct file_info xfinfo; + /* File name to actually use. Might differ in case from + finfo->file. */ + char *filename; send_a_repository ("", finfo->repository, finfo->update_dir); - vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL, - (char *)NULL, - finfo->file, 0, 0, finfo->entries, (RCSNode *)NULL); + xfinfo = *finfo; + xfinfo.repository = NULL; + xfinfo.rcs = NULL; + vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0); + + if (vers->entdata != NULL) + filename = vers->entdata->user; + else + filename = finfo->file; if (vers->vn_user != NULL) { - char *tmp; - - tmp = xmalloc (strlen (finfo->file) + strlen (vers->vn_user) - + strlen (vers->options) + 200); - sprintf (tmp, "Entry /%s/%s/%s%s/%s/", - finfo->file, vers->vn_user, - vers->ts_conflict == NULL ? "" : "+", - (vers->ts_conflict == NULL ? "" - : (vers->ts_user != NULL && - strcmp (vers->ts_conflict, vers->ts_user) == 0 - ? "=" - : "modified")), - vers->options); + char *tmp; + + tmp = xmalloc (strlen (filename) + strlen (vers->vn_user) + + strlen (vers->options) + 200); + sprintf (tmp, "Entry /%s/%s/%s%s/%s/", + filename, vers->vn_user, + vers->ts_conflict == NULL ? "" : "+", + (vers->ts_conflict == NULL ? "" + : (vers->ts_user != NULL && + strcmp (vers->ts_conflict, vers->ts_user) == 0 + ? "=" + : "modified")), + vers->options); /* The Entries request. */ /* Not sure about whether this deals with -k and stuff right. */ @@ -3679,33 +4384,28 @@ send_fileproc (finfo) * Do we want to print "file was lost" like normal CVS? * Would it always be appropriate? */ - /* File no longer exists. */ - if (!use_unchanged) - { - /* if the server is old, use the old request... */ - send_to_server ("Lost ", 0); - send_to_server (finfo->file, 0); - send_to_server ("\012", 1); - /* - * Otherwise, don't do anything for missing files, - * they just happen. - */ - } + /* File no longer exists. Don't do anything, missing files + just happen. */ } else if (vers->ts_rcs == NULL + || args->force || strcmp (vers->ts_user, vers->ts_rcs) != 0) { - send_modified (finfo->file, finfo->fullname, vers); + if (args->no_contents + && supported_request ("Is-modified")) + { + send_to_server ("Is-modified ", 0); + send_to_server (filename, 0); + send_to_server ("\012", 1); + } + else + send_modified (filename, finfo->fullname, vers); } else { - /* Only use this request if the server supports it... */ - if (use_unchanged) - { - send_to_server ("Unchanged ", 0); - send_to_server (finfo->file, 0); - send_to_server ("\012", 1); - } + send_to_server ("Unchanged ", 0); + send_to_server (filename, 0); + send_to_server ("\012", 1); } /* if this directory has an ignore list, add this file to it */ @@ -3745,25 +4445,27 @@ send_ignproc (file, dir) } } -static int send_filesdoneproc PROTO ((int, char *, char *)); +static int send_filesdoneproc PROTO ((void *, int, char *, char *, List *)); static int -send_filesdoneproc (err, repository, update_dir) +send_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { /* if this directory has an ignore list, process it then free it */ if (ignlist) { - ignore_files (ignlist, update_dir, send_ignproc); + ignore_files (ignlist, entries, update_dir, send_ignproc); dellist (&ignlist); } return (err); } -static Dtype send_dirent_proc PROTO ((char *, char *, char *)); +static Dtype send_dirent_proc PROTO ((void *, char *, char *, char *, List *)); /* * send_dirent_proc () is called back by the recursion processor before a @@ -3774,20 +4476,18 @@ static Dtype send_dirent_proc PROTO ((char *, char *, char *)); * */ static Dtype -send_dirent_proc (dir, repository, update_dir) +send_dirent_proc (callerdat, dir, repository, update_dir, entries) + void *callerdat; char *dir; char *repository; char *update_dir; + List *entries; { + struct send_data *args = (struct send_data *) callerdat; int dir_exists; + char *cvsadm_name; char *cvsadm_repos_name; - /* - * If the directory does not exist yet (e.g. "cvs update -d - * foo"), no need to send any files from it. - */ - dir_exists = isdir (dir); - if (ignore_directory (update_dir)) { /* print the warm fuzzy message */ @@ -3796,6 +4496,19 @@ send_dirent_proc (dir, repository, update_dir) return (R_SKIP_ALL); } + /* + * If the directory does not exist yet (e.g. "cvs update -d foo"), + * no need to send any files from it. If the directory does not + * have a CVS directory, then we pretend that it does not exist. + * Otherwise, we will fail when trying to open the Entries file. + * This case will happen when checking out a module defined as + * ``-a .''. + */ + cvsadm_name = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); + sprintf (cvsadm_name, "%s/%s", dir, CVSADM); + dir_exists = isdir (cvsadm_name); + free (cvsadm_name); + /* initialize the ignore list for this directory */ ignlist = getlist (); @@ -3818,7 +4531,14 @@ send_dirent_proc (dir, repository, update_dir) free (repos); } else - send_a_repository (dir, repository, update_dir); + { + /* Don't send a non-existent directory unless we are building + new directories (build_dirs is true). Otherwise, CVS may + see a D line in an Entries file, and recreate a directory + which the user removed by hand. */ + if (dir_exists || args->build_dirs) + send_a_repository (dir, repository, update_dir); + } free (cvsadm_repos_name); return (dir_exists ? R_PROCESS : R_SKIP_ALL); @@ -3826,27 +4546,35 @@ send_dirent_proc (dir, repository, update_dir) /* * Send each option in a string to the server, one by one. - * This assumes that the options are single characters. For - * more complex parsing, do it yourself. + * This assumes that the options are separated by spaces, for example + * STRING might be "--foo -C5 -y". */ void send_option_string (string) char *string; { + char *copy; char *p; - char it[3]; - - for (p = string; p[0]; p++) { - if (p[0] == ' ') - continue; - if (p[0] == '-') - continue; - it[0] = '-'; - it[1] = p[0]; - it[2] = '\0'; - send_arg (it); + + copy = xstrdup (string); + p = copy; + while (1) + { + char *s; + char l; + + for (s = p; *s != ' ' && *s != '\0'; s++) + ; + l = *s; + *s = '\0'; + if (s != p) + send_arg (p); + if (l == '\0') + break; + p = s + 1; } + free (copy); } @@ -3859,8 +4587,6 @@ send_file_names (argc, argv, flags) unsigned int flags; { int i; - char *p; - char *q; int level; int max_level; @@ -3873,25 +4599,9 @@ send_file_names (argc, argv, flags) max_level = 0; for (i = 0; i < argc; ++i) { - p = argv[i]; - level = 0; - do - { - q = strchr (p, '/'); - if (q != NULL) - ++q; - if (p[0] == '.' && p[1] == '.' && (p[2] == '\0' || p[2] == '/')) - { - --level; - if (-level > max_level) - max_level = -level; - } - else if (p[0] == '.' && (p[1] == '\0' || p[1] == '/')) - ; - else - ++level; - p = q; - } while (p != NULL); + level = pathname_levels (argv[i]); + if (level > max_level) + max_level = level; } if (max_level > 0) { @@ -3918,6 +4628,46 @@ send_file_names (argc, argv, flags) { char buf[1]; char *p = argv[i]; + char *line = NULL; + +#ifdef FILENAMES_CASE_INSENSITIVE + /* We want to send the file name as it appears + in CVS/Entries. We put this inside an ifdef + to avoid doing all these system calls in + cases where fncmp is just strcmp anyway. */ + /* For now just do this for files in the local + directory. Would be nice to handle the + non-local case too, though. */ + /* The isdir check could more gracefully be replaced + with a way of having Entries_Open report back the + error to us and letting us ignore existence_error. + Or some such. */ + if (p == last_component (p) && isdir (CVSADM)) + { + List *entries; + Node *node; + + /* If we were doing non-local directory, + we would save_cwd, CVS_CHDIR + like in update.c:isemptydir. */ + /* Note that if we are adding a directory, + the following will read the entry + that we just wrote there, that is, we + will get the case specified on the + command line, not the case of the + directory in the filesystem. This + is correct behavior. */ + entries = Entries_Open (0); + node = findnode_fn (entries, p); + if (node != NULL) + { + line = xstrdup (node->key); + p = line; + delnode (node); + } + Entries_Close (entries); + } +#endif /* FILENAMES_CASE_INSENSITIVE */ send_to_server ("Argument ", 0); @@ -3940,6 +4690,8 @@ send_file_names (argc, argv, flags) ++p; } send_to_server ("\012", 1); + if (line != NULL) + free (line); } if (flags & SEND_EXPAND_WILD) @@ -3952,20 +4704,24 @@ send_file_names (argc, argv, flags) } -/* - * Send Repository, Modified and Entry. argc and argv contain only - * the files to operate on (or empty for everything), not options. - * local is nonzero if we should not recurse (-l option). Also sends - * Argument lines for argc and argv, so should be called after options - * are sent. - */ +/* Send Repository, Modified and Entry. argc and argv contain only + the files to operate on (or empty for everything), not options. + local is nonzero if we should not recurse (-l option). flags & + SEND_BUILD_DIRS is nonzero if nonexistent directories should be + sent. flags & SEND_FORCE is nonzero if we should send unmodified + files to the server as though they were modified. flags & + SEND_NO_CONTENTS means that this command only needs to know + _whether_ a file is modified, not the contents. Also sends Argument + lines for argc and argv, so should be called after options are sent. */ void -send_files (argc, argv, local, aflag) +send_files (argc, argv, local, aflag, flags) int argc; char **argv; int local; int aflag; + unsigned int flags; { + struct send_data args; int err; /* @@ -3973,12 +4729,15 @@ send_files (argc, argv, local, aflag) * But we don't actually use it, so I don't think it matters what we pass * for aflag here. */ + args.build_dirs = flags & SEND_BUILD_DIRS; + args.force = flags & SEND_FORCE; + args.no_contents = flags & SEND_NO_CONTENTS; err = start_recursion (send_fileproc, send_filesdoneproc, - send_dirent_proc, (DIRLEAVEPROC)NULL, - argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0, 0); + send_dirent_proc, (DIRLEAVEPROC)NULL, (void *) &args, + argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0); if (err) - exit (EXIT_FAILURE); + error_exit (); if (toplevel_repos == NULL) /* * This happens if we are not processing any files, @@ -3987,7 +4746,7 @@ send_files (argc, argv, local, aflag) * latter case; I don't think toplevel_repos matters for the * former. */ - toplevel_repos = xstrdup (server_cvsroot); + toplevel_repos = xstrdup (CVSroot_directory); send_repository ("", toplevel_repos, "."); } @@ -4003,36 +4762,56 @@ client_import_setup (repository) * Process the argument import file. */ int -client_process_import_file (message, vfile, vtag, targc, targv, repository) +client_process_import_file (message, vfile, vtag, targc, targv, repository, + all_files_binary) char *message; char *vfile; char *vtag; int targc; char *targv[]; char *repository; + int all_files_binary; { - char *short_pathname; - int first_time; - - /* FIXME: I think this is always false now that we call - client_import_setup at the start. */ - - first_time = toplevel_repos == NULL; + char *update_dir; + char *fullname; + Vers_TS vers; - if (first_time) - send_a_repository ("", repository, ""); + assert (toplevel_repos != NULL); if (strncmp (repository, toplevel_repos, strlen (toplevel_repos)) != 0) error (1, 0, "internal error: pathname `%s' doesn't specify file in `%s'", repository, toplevel_repos); - short_pathname = repository + strlen (toplevel_repos) + 1; - if (!first_time) + if (strcmp (repository, toplevel_repos) == 0) + { + update_dir = ""; + fullname = xstrdup (vfile); + } + else + { + update_dir = repository + strlen (toplevel_repos) + 1; + + fullname = xmalloc (strlen (vfile) + strlen (update_dir) + 10); + strcpy (fullname, update_dir); + strcat (fullname, "/"); + strcat (fullname, vfile); + } + + send_a_repository ("", repository, update_dir); + if (all_files_binary) + { + vers.options = xmalloc (4); /* strlen("-kb") + 1 */ + strcpy (vers.options, "-kb"); + } + else { - send_a_repository ("", repository, short_pathname); + vers.options = wrap_rcsoption (vfile, 1); } - send_modified (vfile, short_pathname, NULL); + send_modified (vfile, fullname, &vers); + if (vers.options != NULL) + free (vers.options); + free (fullname); return 0; } @@ -4049,7 +4828,7 @@ client_import_done () */ /* FIXME: "can't happen" now that we call client_import_setup at the beginning. */ - toplevel_repos = xstrdup (server_cvsroot); + toplevel_repos = xstrdup (CVSroot_directory); send_repository ("", toplevel_repos, "."); } @@ -4092,9 +4871,10 @@ notified_a_file (data, ent_list, short_pathname, filename) { if (feof (fp)) { + free (line); if (fclose (fp) < 0) error (0, errno, "cannot close %s", CVSADM_NOTIFY); - if (unlink (CVSADM_NOTIFY) < 0) + if ( CVS_UNLINK (CVSADM_NOTIFY) < 0) error (0, errno, "cannot remove %s", CVSADM_NOTIFY); return; } @@ -4134,6 +4914,7 @@ notified_a_file (data, ent_list, short_pathname, filename) error (0, errno, "cannot close %s", CVSADM_NOTIFYTMP); goto error_exit; } + free (line); if (fclose (fp) < 0) { error (0, errno, "cannot close %s", CVSADM_NOTIFY); @@ -4152,6 +4933,7 @@ notified_a_file (data, ent_list, short_pathname, filename) error2: (void) fclose (newf); error_exit: + free (line); (void) fclose (fp); } @@ -4234,257 +5016,13 @@ client_senddate (date) option_with_arg ("-D", buf); } -int -client_commit (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return commit (argc, argv); -} - -int -client_update (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return update (argc, argv); -} - -int -client_checkout (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return checkout (argc, argv); -} - -int -client_diff (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return diff (argc, argv); /* Call real code */ -} - -int -client_status (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - return status (argc, argv); -} - -int -client_log (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return cvslog (argc, argv); /* Call real code */ -} - -int -client_add (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return add (argc, argv); /* Call real code */ -} - -int -client_remove (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return cvsremove (argc, argv); /* Call real code */ -} - -int -client_rdiff (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return patch (argc, argv); /* Call real code */ -} - -int -client_tag (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return tag (argc, argv); /* Call real code */ -} - -int -client_rtag (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return rtag (argc, argv); /* Call real code */ -} - -int -client_import (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return import (argc, argv); /* Call real code */ -} - -int -client_admin (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return admin (argc, argv); /* Call real code */ -} - -int -client_export (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return checkout (argc, argv); /* Call real code */ -} - -int -client_history (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return history (argc, argv); /* Call real code */ -} - -int -client_release (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return release (argc, argv); /* Call real code */ -} - -int -client_watch (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return watch (argc, argv); /* Call real code */ -} - -int -client_watchers (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return watchers (argc, argv); /* Call real code */ -} - -int -client_editors (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return editors (argc, argv); /* Call real code */ -} - -int -client_edit (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return edit (argc, argv); /* Call real code */ -} - -int -client_unedit (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return unedit (argc, argv); /* Call real code */ -} - void send_init_command () { - /* This is here because we need the server_cvsroot variable. */ + /* This is here because we need the CVSroot_directory variable. */ send_to_server ("init ", 0); - send_to_server (server_cvsroot, 0); + send_to_server (CVSroot_directory, 0); send_to_server ("\012", 0); } -int -client_init (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return init (argc, argv); /* Call real code */ -} - -int -client_annotate (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return annotate (argc, argv); /* Call real code */ -} #endif /* CLIENT_SUPPORT */ diff --git a/contrib/cvs/src/client.h b/contrib/cvs/src/client.h index b238c32..1f9c27f 100644 --- a/contrib/cvs/src/client.h +++ b/contrib/cvs/src/client.h @@ -5,45 +5,27 @@ extern char *mode_to_string PROTO((mode_t)); extern int change_mode PROTO((char *, char *)); extern int gzip_level; +extern int file_gzip_level; extern int filter_through_gzip PROTO((int, int, int, pid_t *)); extern int filter_through_gunzip PROTO((int, int, pid_t *)); -#ifdef CLIENT_SUPPORT -/* - * Functions to perform CVS commands via the protocol. argc and argv - * are the arguments and the return value is the exit status (zero success - * nonzero failure). - */ -extern int client_commit PROTO((int argc, char **argv)); -extern int client_update PROTO((int argc, char **argv)); -extern int client_checkout PROTO((int argc, char **argv)); -extern int client_diff PROTO((int argc, char **argv)); -extern int client_log PROTO((int argc, char **argv)); -extern int client_add PROTO((int argc, char **argv)); -extern int client_remove PROTO((int argc, char **argv)); -extern int client_status PROTO((int argc, char **argv)); -extern int client_rdiff PROTO((int argc, char **argv)); -extern int client_tag PROTO((int argc, char **argv)); -extern int client_rtag PROTO((int argc, char **argv)); -extern int client_import PROTO((int argc, char **argv)); -extern int client_admin PROTO((int argc, char **argv)); -extern int client_export PROTO((int argc, char **argv)); -extern int client_history PROTO((int argc, char **argv)); -extern int client_release PROTO((int argc, char **argv)); -extern int client_watch PROTO((int argc, char **argv)); -extern int client_watchers PROTO((int argc, char **argv)); -extern int client_editors PROTO((int argc, char **argv)); -extern int client_edit PROTO((int argc, char **argv)); -extern int client_unedit PROTO((int argc, char **argv)); -extern int client_init PROTO ((int argc, char **argv)); -extern int client_annotate PROTO ((int argc, char **argv)); +#if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) -/* - * Flag variable for seeing whether common code is running as a client - * or to do a local operation. - */ -extern int client_active; +extern int cvsencrypt; + +#ifdef ENCRYPTION +#ifdef HAVE_KERBEROS + +/* We can't declare the arguments without including krb.h, and I don't + want to do that in every file. */ +extern struct buffer *krb_encrypt_buffer_initialize (); + +#endif /* HAVE_KERBEROS */ +#endif /* ENCRYPTION */ +#endif /* defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */ + +#ifdef CLIENT_SUPPORT /* * Flag variable for seeing whether the server has been started yet. * As of this writing, only edit.c:notify_check() uses it. @@ -62,7 +44,11 @@ int connect_to_pserver PROTO((int *tofdp, int* fromfdp, int verify_only)); #endif /* AUTH_CLIENT_SUPPORT */ #ifdef AUTH_SERVER_SUPPORT -extern void authenticate_connection PROTO ((void)); +extern void pserver_authenticate_connection PROTO ((void)); +#endif + +#if defined (SERVER_SUPPORT) && defined (HAVE_KERBEROS) +extern void kserver_authenticate_connection PROTO ((void)); #endif /* Talking to the server. */ @@ -96,16 +82,13 @@ send_file_names PROTO((int argc, char **argv, unsigned int flags)); * local is nonzero if we should not recurse (-l option). */ void -send_files PROTO((int argc, char **argv, int local, int aflag)); +send_files PROTO((int argc, char **argv, int local, int aflag, + unsigned int flags)); -/* - * Like send_files but never send "Unchanged"--just send the contents of the - * file in that case. This is used to fix it if you import a directory which - * happens to have CVS directories (yes it is obscure but the testsuite tests - * it). - */ -void -send_files_contents PROTO((int argc, char **argv, int local, int aflag)); +/* Flags for send_files. */ +#define SEND_BUILD_DIRS 1 +#define SEND_FORCE 2 +#define SEND_NO_CONTENTS 4 /* Send an argument to the remote server. */ void @@ -174,18 +157,19 @@ extern struct response responses[]; extern void client_senddate PROTO((const char *date)); extern void client_expand_modules PROTO((int argc, char **argv, int local)); -extern void client_send_expansions PROTO((int local)); +extern void client_send_expansions PROTO((int local, char *where, + int build_dirs)); extern void client_nonexpanded_setup PROTO((void)); extern void send_init_command PROTO ((void)); extern char **failed_patches; extern int failed_patches_count; -extern char toplevel_wd[]; +extern char *toplevel_wd; extern void client_import_setup PROTO((char *repository)); extern int client_process_import_file PROTO((char *message, char *vfile, char *vtag, - int targc, char *targv[], char *repository)); + int targc, char *targv[], char *repository, int all_files_binary)); extern void client_import_done PROTO((void)); extern void client_notify PROTO((char *, char *, char *, int, char *)); #endif /* CLIENT_SUPPORT */ diff --git a/contrib/cvs/src/commit.c b/contrib/cvs/src/commit.c index ac81790..c0c3597 100644 --- a/contrib/cvs/src/commit.c +++ b/contrib/cvs/src/commit.c @@ -14,37 +14,47 @@ * */ +#include <assert.h> #include "cvs.h" #include "getline.h" #include "edit.h" #include "fileattr.h" -static Dtype check_direntproc PROTO((char *dir, char *repos, char *update_dir)); -static int check_fileproc PROTO((struct file_info *finfo)); -static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); +static Dtype check_direntproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int check_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static int checkaddfile PROTO((char *file, char *repository, char *tag, char *options, RCSNode **rcsnode)); -static Dtype commit_direntproc PROTO((char *dir, char *repos, char *update_dir)); -static int commit_dirleaveproc PROTO((char *dir, int err, char *update_dir)); -static int commit_fileproc PROTO((struct file_info *finfo)); -static int commit_filesdoneproc PROTO((int err, char *repository, char *update_dir)); -static int finaladd PROTO((char *file, char *revision, char *tag, - char *options, char *update_dir, - char *repository, List *entries)); +static Dtype commit_direntproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int commit_dirleaveproc PROTO ((void *callerdat, char *dir, + int err, char *update_dir, + List *entries)); +static int commit_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int commit_filesdoneproc PROTO ((void *callerdat, int err, + char *repository, char *update_dir, + List *entries)); +static int finaladd PROTO((struct file_info *finfo, char *revision, char *tag, + char *options)); static int findmaxrev PROTO((Node * p, void *closure)); -static int lock_RCS PROTO((char *user, char *rcs, char *rev, char *repository)); -static int lockrcsfile PROTO((char *file, char *repository, char *rev)); +static int lock_RCS PROTO((char *user, RCSNode *rcs, char *rev, + char *repository)); static int precommit_list_proc PROTO((Node * p, void *closure)); static int precommit_proc PROTO((char *repository, char *filter)); -static int remove_file PROTO((char *file, char *repository, char *tag, - char *message, List *entries, RCSNode *rcsnode)); +static int remove_file PROTO ((struct file_info *finfo, char *tag, + char *message)); static void fix_rcs_modes PROTO((char *rcs, char *user)); static void fixaddfile PROTO((char *file, char *repository)); -static void fixbranch PROTO((char *file, char *repository, char *branch)); -static void unlockrcs PROTO((char *file, char *repository)); +static void fixbranch PROTO((RCSNode *, char *branch)); +static void unlockrcs PROTO((RCSNode *rcs)); static void ci_delproc PROTO((Node *p)); static void masterlist_delproc PROTO((Node *p)); -static void locate_rcs PROTO((char *file, char *repository, char *rcs)); +static char *locate_rcs PROTO((char *file, char *repository)); struct commit_info { @@ -65,6 +75,7 @@ static int run_module_prog = 1; static int aflag; static char *tag; static char *write_dirtag; +static int write_dirnonbranch; static char *logfile; static List *mulist; static char *message; @@ -85,37 +96,150 @@ static const char *const commit_usage[] = }; #ifdef CLIENT_SUPPORT +/* Identify a file which needs "? foo" or a Questionable request. */ +struct question { + /* The two fields for the Directory request. */ + char *dir; + char *repos; + + /* The file name. */ + char *file; + + struct question *next; +}; + struct find_data { List *ulist; int argc; char **argv; + + /* This is used from dirent to filesdone time, for each directory, + to make a list of files we have already seen. */ + List *ignlist; + + /* Linked list of files which need "? foo" or a Questionable request. */ + struct question *questionables; + + /* Only good within functions called from the filesdoneproc. Stores + the repository (pointer into storage managed by the recursion + processor. */ + char *repository; + + /* Non-zero if we should force the commit. This is enabled by + either -f or -r options, unlike force_ci which is just -f. */ + int force; }; -/* Pass as a static until we get around to fixing start_recursion to - pass along a void * where we can stash it. */ -struct find_data *find_data_static; +static Dtype find_dirent_proc PROTO ((void *callerdat, char *dir, + char *repository, char *update_dir, + List *entries)); + +static Dtype +find_dirent_proc (callerdat, dir, repository, update_dir, entries) + void *callerdat; + char *dir; + char *repository; + char *update_dir; + List *entries; +{ + struct find_data *find_data = (struct find_data *)callerdat; + + /* initialize the ignore list for this directory */ + find_data->ignlist = getlist (); + return R_PROCESS; +} + +/* Here as a static until we get around to fixing ignore_files to pass + it along as an argument. */ +static struct find_data *find_data_static; + +static void find_ignproc PROTO ((char *, char *)); + +static void +find_ignproc (file, dir) + char *file; + char *dir; +{ + struct question *p; + + p = (struct question *) xmalloc (sizeof (struct question)); + p->dir = xstrdup (dir); + p->repos = xstrdup (find_data_static->repository); + p->file = xstrdup (file); + p->next = find_data_static->questionables; + find_data_static->questionables = p; +} + +static int find_filesdoneproc PROTO ((void *callerdat, int err, + char *repository, char *update_dir, + List *entries)); + +static int +find_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; + int err; + char *repository; + char *update_dir; + List *entries; +{ + struct find_data *find_data = (struct find_data *)callerdat; + find_data->repository = repository; + + /* if this directory has an ignore list, process it then free it */ + if (find_data->ignlist) + { + find_data_static = find_data; + ignore_files (find_data->ignlist, entries, update_dir, find_ignproc); + dellist (&find_data->ignlist); + } + + find_data->repository = NULL; + + return err; +} -static int find_fileproc PROTO ((struct file_info *finfo)); +static int find_fileproc PROTO ((void *callerdat, struct file_info *finfo)); /* Machinery to find out what is modified, added, and removed. It is possible this should be broken out into a new client_classify function; merging it with classify_file is almost sure to be a mess, though, because classify_file has all kinds of repository processing. */ static int -find_fileproc (finfo) +find_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { Vers_TS *vers; enum classify_type status; Node *node; - struct find_data *args = find_data_static; + struct find_data *args = (struct find_data *)callerdat; + struct logfile_info *data; + struct file_info xfinfo; + + /* if this directory has an ignore list, add this file to it */ + if (args->ignlist) + { + Node *p; + + p = getnode (); + p->type = FILES; + p->key = xstrdup (finfo->file); + if (addnode (args->ignlist, p) != 0) + freenode (p); + } - vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL, - (char *)NULL, - finfo->file, 0, 0, finfo->entries, (RCSNode *)NULL); + xfinfo = *finfo; + xfinfo.repository = NULL; + xfinfo.rcs = NULL; + + vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0); if (vers->ts_user == NULL && vers->vn_user != NULL && vers->vn_user[0] == '-') + /* FIXME: If vn_user is starts with "-" but ts_user is + non-NULL, what classify_file does is print "%s should be + removed and is still there". I'm not sure what it does + then. We probably should do the same. */ status = T_REMOVED; else if (vers->vn_user == NULL) { @@ -129,10 +253,15 @@ find_fileproc (finfo) else if (vers->ts_user != NULL && vers->vn_user != NULL && vers->vn_user[0] == '0') + /* FIXME: If vn_user is "0" but ts_user is NULL, what classify_file + does is print "new-born %s has disappeared" and removes the entry. + We probably should do the same. */ status = T_ADDED; else if (vers->ts_user != NULL && vers->ts_rcs != NULL - && strcmp (vers->ts_user, vers->ts_rcs) != 0) + && (args->force || strcmp (vers->ts_user, vers->ts_rcs) != 0)) + /* If we are forcing commits, pretend that the file is + modified. */ status = T_MODIFIED; else { @@ -146,13 +275,19 @@ find_fileproc (finfo) node = getnode (); node->key = xstrdup (finfo->fullname); + data = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); + data->type = status; + data->tag = xstrdup (vers->tag); + data->rev_old = data->rev_new = NULL; + node->type = UPDATE; node->delproc = update_delproc; - node->data = (char *) status; + node->data = (char *) data; (void)addnode (args->ulist, node); ++args->argc; + freevers_ts (&vers); return 0; } @@ -198,7 +333,7 @@ commit (argc, argv) #endif /* CVS_BADROOT */ optind = 1; - while ((c = getopt (argc, argv, "nlRm:fF:r:")) != -1) + while ((c = getopt (argc, argv, "+nlRm:fF:r:")) != -1) { switch (c) { @@ -270,7 +405,7 @@ commit (argc, argv) error (1, 0, "cannot specify both a message and a log file"); /* FIXME: Why is this binary? Needs more investigation. */ - if ((logfd = open (logfile, O_RDONLY | OPEN_BINARY)) < 0) + if ((logfd = CVS_OPEN (logfile, O_RDONLY | OPEN_BINARY)) < 0) error (1, errno, "cannot open log file %s", logfile); if (fstat(logfd, &statbuf) < 0) @@ -294,21 +429,31 @@ commit (argc, argv) ign_setup (); - /* Note that we don't do ignore file processing here, and we - don't call ignore_files. This means that we won't print "? - foo" for stray files. Sounds OK, the doc only promises - that update does that. */ find_args.ulist = getlist (); find_args.argc = 0; - find_data_static = &find_args; - err = start_recursion (find_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, - argc, argv, local, W_LOCAL, 0, 0, - (char *)NULL, 0, 0); + find_args.questionables = NULL; + find_args.ignlist = NULL; + find_args.repository = NULL; + + /* It is possible that only a numeric tag should set this. + I haven't really thought about it much. + Anyway, I suspect that setting it unnecessarily only causes + a little unneeded network traffic. */ + find_args.force = force_ci || tag != NULL; + + err = start_recursion (find_fileproc, find_filesdoneproc, + find_dirent_proc, (DIRLEAVEPROC) NULL, + (void *)&find_args, + argc, argv, local, W_LOCAL, 0, 0, + (char *)NULL, 0); if (err) error (1, 0, "correct above errors first!"); if (find_args.argc == 0) + /* Nothing to commit. Exit now without contacting the + server (note that this means that we won't print "? + foo" for files which merit it, because we don't know + what is in the CVSROOT/cvsignore file). */ return 0; /* Now we keep track of which files we actually are going to @@ -319,12 +464,16 @@ commit (argc, argv) find_args.argc = 0; walklist (find_args.ulist, copy_ulist, &find_args); - /* - * Do this before calling do_editor; don't ask for a log - * message if we can't talk to the server. But do it after we - * have made the checks that we can locally (to more quickly - * catch syntax errors, the case where no files are modified, - * added or removed, etc.). */ + /* Do this before calling do_editor; don't ask for a log + message if we can't talk to the server. But do it after we + have made the checks that we can locally (to more quickly + catch syntax errors, the case where no files are modified, + added or removed, etc.). + + On the other hand, calling start_server before do_editor + means that we chew up server resources the whole time that + the user has the editor open (hours or days if the user + forgets about it), which seems dubious. */ start_server (); /* @@ -334,9 +483,55 @@ commit (argc, argv) if (use_editor) do_editor (".", &message, (char *)NULL, find_args.ulist); + /* Run the user-defined script to verify/check information in + *the log message + */ + do_verify (message, (char *)NULL); + /* We always send some sort of message, even if empty. */ option_with_arg ("-m", message); + /* OK, now process all the questionable files we have been saving + up. */ + { + struct question *p; + struct question *q; + + p = find_args.questionables; + while (p != NULL) + { + if (ign_inhibit_server || !supported_request ("Questionable")) + { + cvs_output ("? ", 2); + if (p->dir[0] != '\0') + { + cvs_output (p->dir, 0); + cvs_output ("/", 1); + } + cvs_output (p->file, 0); + cvs_output ("\n", 1); + } + else + { + send_to_server ("Directory ", 0); + send_to_server (p->dir[0] == '\0' ? "." : p->dir, 0); + send_to_server ("\012", 1); + send_to_server (p->repos, 0); + send_to_server ("\012", 1); + + send_to_server ("Questionable ", 0); + send_to_server (p->file, 0); + send_to_server ("\012", 1); + } + free (p->dir); + free (p->repos); + free (p->file); + q = p->next; + free (p); + p = q; + } + } + if (local) send_arg("-l"); if (force_ci) @@ -351,7 +546,18 @@ commit (argc, argv) previous versions of client/server CVS, but it probably is a Good Thing, or at least Not Such A Bad Thing. */ send_file_names (find_args.argc, find_args.argv, 0); - send_files (find_args.argc, find_args.argv, local, 0); + + /* FIXME: This whole find_args.force/SEND_FORCE business is a + kludge. It would seem to be a server bug that we have to + say that files are modified when they are not. This makes + "cvs commit -r 2" across a whole bunch of files a very slow + operation (and it isn't documented in cvsclient.texi). I + haven't looked at the server code carefully enough to be + _sure_ why this is needed, but if it is because RCS_CI + wants the file to exist, then it would be relatively simple + (but not trivial) to fix in the server. */ + send_files (find_args.argc, find_args.argv, local, 0, + find_args.force ? SEND_FORCE : 0); send_to_server ("ci\012", 0); return get_responses_and_close (); @@ -378,28 +584,28 @@ commit (argc, argv) * Run the recursion processor to verify the files are all up-to-date */ err = start_recursion (check_fileproc, check_filesdoneproc, - check_direntproc, (DIRLEAVEPROC) NULL, argc, - argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1, - 0); + check_direntproc, (DIRLEAVEPROC) NULL, NULL, argc, + argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1); if (err) { - lock_tree_cleanup (); + Lock_Cleanup (); error (1, 0, "correct above errors first!"); } /* * Run the recursion processor to commit the files */ + write_dirnonbranch = 0; if (noexec == 0) err = start_recursion (commit_fileproc, commit_filesdoneproc, - commit_direntproc, commit_dirleaveproc, + commit_direntproc, commit_dirleaveproc, NULL, argc, argv, local, W_LOCAL, aflag, 0, - (char *) NULL, 1, 0); + (char *) NULL, 1); /* * Unlock all the dirs and clean up */ - lock_tree_cleanup (); + Lock_Cleanup (); dellist (&mulist); if (last_register_time) @@ -416,23 +622,18 @@ commit (argc, argv) return (err); } -/* - * Check to see if a file is ok to commit and make sure all files are - * up-to-date - */ -/* ARGSUSED */ -static int -check_fileproc (finfo) +/* This routine determines the status of a given file and retrieves + the version information that is associated with that file. */ + +static +Ctype +classify_file_internal (finfo, vers) struct file_info *finfo; + Vers_TS **vers; { - Ctype status; - char *xdir; - Node *p; - List *ulist, *cilist; - Vers_TS *vers; - struct commit_info *ci; int save_noexec, save_quiet, save_really_quiet; - + Ctype status; + save_noexec = noexec; save_quiet = quiet; save_really_quiet = really_quiet; @@ -444,19 +645,16 @@ check_fileproc (finfo) /* If the tag is for the trunk, make sure we're at the head */ if (numdots (tag) < 2) { - status = Classify_File (finfo->file, (char *) NULL, (char *) NULL, - (char *) NULL, 1, aflag, finfo->repository, - finfo->entries, finfo->rcs, &vers, finfo->update_dir, 0); + status = Classify_File (finfo, (char *) NULL, (char *) NULL, + (char *) NULL, 1, aflag, vers, 0); if (status == T_UPTODATE || status == T_MODIFIED || status == T_ADDED) { Ctype xstatus; - freevers_ts (&vers); - xstatus = Classify_File (finfo->file, tag, (char *) NULL, - (char *) NULL, 1, aflag, finfo->repository, - finfo->entries, finfo->rcs, &vers, finfo->update_dir, - 0); + freevers_ts (vers); + xstatus = Classify_File (finfo, tag, (char *) NULL, + (char *) NULL, 1, aflag, vers, 0); if (xstatus == T_REMOVE_ENTRY) status = T_MODIFIED; else if (status == T_MODIFIED && xstatus == T_CONFLICT) @@ -479,36 +677,55 @@ check_fileproc (finfo) cp = strrchr (xtag, '.'); *cp = '\0'; } - status = Classify_File (finfo->file, xtag, (char *) NULL, - (char *) NULL, 1, aflag, finfo->repository, - finfo->entries, finfo->rcs, &vers, finfo->update_dir, 0); + status = Classify_File (finfo, xtag, (char *) NULL, + (char *) NULL, 1, aflag, vers, 0); if ((status == T_REMOVE_ENTRY || status == T_CONFLICT) && (cp = strrchr (xtag, '.')) != NULL) { /* pluck one more dot off the revision */ *cp = '\0'; - freevers_ts (&vers); - status = Classify_File (finfo->file, xtag, (char *) NULL, - (char *) NULL, 1, aflag, finfo->repository, - finfo->entries, finfo->rcs, &vers, finfo->update_dir, - 0); + freevers_ts (vers); + status = Classify_File (finfo, xtag, (char *) NULL, + (char *) NULL, 1, aflag, vers, 0); if (status == T_UPTODATE || status == T_REMOVE_ENTRY) status = T_MODIFIED; } /* now, muck with vers to make the tag correct */ - free (vers->tag); - vers->tag = xstrdup (tag); + free ((*vers)->tag); + (*vers)->tag = xstrdup (tag); free (xtag); } } else - status = Classify_File (finfo->file, tag, (char *) NULL, (char *) NULL, - 1, 0, finfo->repository, finfo->entries, finfo->rcs, &vers, - finfo->update_dir, 0); + status = Classify_File (finfo, tag, (char *) NULL, (char *) NULL, + 1, 0, vers, 0); noexec = save_noexec; quiet = save_quiet; really_quiet = save_really_quiet; + return status; +} + +/* + * Check to see if a file is ok to commit and make sure all files are + * up-to-date + */ +/* ARGSUSED */ +static int +check_fileproc (callerdat, finfo) + void *callerdat; + struct file_info *finfo; +{ + Ctype status; + char *xdir; + Node *p; + List *ulist, *cilist; + Vers_TS *vers; + struct commit_info *ci; + struct logfile_info *li; + + status = classify_file_internal (finfo, &vers); + /* * If the force-commit option is enabled, and the file in question * appears to be up-to-date, just make it look modified so that @@ -538,7 +755,8 @@ check_fileproc (finfo) * - can't have a sticky tag that is not a branch * Also, * - if status is T_REMOVED, can't have a numeric tag - * - if status is T_ADDED, rcs file must not exist + * - if status is T_ADDED, rcs file must not exist unless on + * a branch * - if status is T_ADDED, can't have a non-trunk numeric rev * - if status is T_MODIFIED and a Conflict marker exists, don't * allow the commit if timestamp is identical or if we find @@ -597,22 +815,7 @@ check_fileproc (finfo) return (1); } - /* - * If the timestamps differ, look for Conflict indicators - * in the file to see if we should block the commit anyway - */ - run_setup ("%s", GREP); - run_arg (RCS_MERGE_PAT); - run_arg (finfo->file); - retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_REALLY); - - if (retcode == -1) - { - error (1, errno, - "fork failed while examining conflict in `%s'", - finfo->fullname); - } - else if (retcode == 0) + if (file_has_markers (finfo)) { error (0, 0, "file `%s' still contains conflict indicators", @@ -632,18 +835,29 @@ check_fileproc (finfo) } if (status == T_ADDED) { - char rcs[PATH_MAX]; - - /* Don't look in the attic; if it exists there we will - move it back out in checkaddfile. */ - sprintf(rcs, "%s/%s%s", finfo->repository, finfo->file, RCSEXT); - if (isreadable (rcs)) + if (vers->tag == NULL) { - error (0, 0, - "cannot add file `%s' when RCS file `%s' already exists", - finfo->fullname, rcs); - freevers_ts (&vers); - return (1); + char *rcs; + + rcs = xmalloc (strlen (finfo->repository) + + strlen (finfo->file) + + sizeof RCSEXT + + 5); + + /* Don't look in the attic; if it exists there we + will move it back out in checkaddfile. */ + sprintf(rcs, "%s/%s%s", finfo->repository, finfo->file, + RCSEXT); + if (isreadable (rcs)) + { + error (0, 0, + "cannot add file `%s' when RCS file `%s' already exists", + finfo->fullname, rcs); + freevers_ts (&vers); + free (rcs); + return (1); + } + free (rcs); } if (vers->tag && isdigit (*vers->tag) && numdots (vers->tag) > 1) @@ -689,7 +903,13 @@ check_fileproc (finfo) p->key = xstrdup (finfo->file); p->type = UPDATE; p->delproc = update_delproc; - p->data = (char *) status; + li = ((struct logfile_info *) + xmalloc (sizeof (struct logfile_info))); + li->type = status; + li->tag = xstrdup (vers->tag); + li->rev_old = xstrdup (vers->vn_rcs); + li->rev_new = NULL; + p->data = (char *) li; (void) addnode (ulist, p); p = getnode (); @@ -730,10 +950,12 @@ check_fileproc (finfo) */ /* ARGSUSED */ static Dtype -check_direntproc (dir, repos, update_dir) +check_direntproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "Examining %s", update_dir); @@ -749,8 +971,12 @@ precommit_list_proc (p, closure) Node *p; void *closure; { - if (p->data == (char *) T_ADDED || p->data == (char *) T_MODIFIED || - p->data == (char *) T_REMOVED) + struct logfile_info *li; + + li = (struct logfile_info *) p->data; + if (li->type == T_ADDED + || li->type == T_MODIFIED + || li->type == T_REMOVED) { run_arg (p->key); } @@ -797,10 +1023,12 @@ precommit_proc (repository, filter) */ /* ARGSUSED */ static int -check_filesdoneproc (err, repos, update_dir) +check_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; int err; char *repos; char *update_dir; + List *entries; { int n; Node *p; @@ -830,18 +1058,33 @@ check_filesdoneproc (err, repos, update_dir) * Do the work of committing a file */ static int maxrev; -static char sbranch[PATH_MAX]; +static char *sbranch; /* ARGSUSED */ static int -commit_fileproc (finfo) +commit_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { Node *p; int err = 0; List *ulist, *cilist; struct commit_info *ci; - char rcs[PATH_MAX]; + + /* Keep track of whether write_dirtag is a branch tag. + Note that if it is a branch tag in some files and a nonbranch tag + in others, treat it as a nonbranch tag. It is possible that case + should elicit a warning or an error. */ + if (write_dirtag != NULL + && finfo->rcs != NULL) + { + char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL); + if (rev != NULL + && !RCS_nodeisbranch (finfo->rcs, write_dirtag)) + write_dirnonbranch = 1; + if (rev != NULL) + free (rev); + } if (finfo->update_dir[0] == '\0') p = findnode (mulist, "."); @@ -862,11 +1105,13 @@ commit_fileproc (finfo) * with files as args from the command line. In that latter case, we * need to get the commit message ourselves */ - if (use_editor && !got_message) - { + if (!(got_message)) + { got_message = 1; - do_editor (finfo->update_dir, &message, finfo->repository, ulist); - } + if (use_editor) + do_editor (finfo->update_dir, &message, finfo->repository, ulist); + do_verify (message, finfo->repository); + } p = findnode (cilist, finfo->file); if (p == NULL) @@ -875,9 +1120,12 @@ commit_fileproc (finfo) ci = (struct commit_info *) p->data; if (ci->status == T_MODIFIED) { - if (lockrcsfile (finfo->file, finfo->repository, ci->rev) != 0) + if (finfo->rcs == NULL) + error (1, 0, "internal error: no parsed RCS file"); + if (lock_RCS (finfo->file, finfo->rcs, ci->rev, + finfo->repository) != 0) { - unlockrcs (finfo->file, finfo->repository); + unlockrcs (finfo->rcs); err = 1; goto out; } @@ -896,15 +1144,17 @@ commit_fileproc (finfo) Since the branch test was done in check_fileproc for modified files, we need to stub it in again here. */ - if (ci->tag) { - locate_rcs (finfo->file, finfo->repository, rcs); + if (ci->tag) + { + if (finfo->rcs == NULL) + error (1, 0, "internal error: no parsed RCS file"); ci->rev = RCS_whatbranch (finfo->rcs, ci->tag); - err = Checkin ('A', finfo->file, finfo->update_dir, finfo->repository, rcs, ci->rev, - ci->tag, ci->options, message, finfo->entries); + err = Checkin ('A', finfo, finfo->rcs->path, ci->rev, + ci->tag, ci->options, message); if (err != 0) { - unlockrcs (finfo->file, finfo->repository); - fixbranch (finfo->file, finfo->repository, sbranch); + unlockrcs (finfo->rcs); + fixbranch (finfo->rcs, sbranch); } (void) time (&last_register_time); @@ -932,36 +1182,37 @@ commit_fileproc (finfo) } /* XXX - an added file with symbolic -r should add tag as well */ - err = finaladd (finfo->file, ci->rev ? ci->rev : xrev, ci->tag, ci->options, - finfo->update_dir, finfo->repository, finfo->entries); + err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options); if (xrev) free (xrev); } else if (ci->status == T_MODIFIED) { - locate_rcs (finfo->file, finfo->repository, rcs); - err = Checkin ('M', finfo->file, finfo->update_dir, finfo->repository, - rcs, ci->rev, ci->tag, - ci->options, message, finfo->entries); + err = Checkin ('M', finfo, + finfo->rcs->path, ci->rev, ci->tag, + ci->options, message); (void) time (&last_register_time); if (err != 0) { - unlockrcs (finfo->file, finfo->repository); - fixbranch (finfo->file, finfo->repository, sbranch); + unlockrcs (finfo->rcs); + fixbranch (finfo->rcs, sbranch); } } else if (ci->status == T_REMOVED) { - err = remove_file (finfo->file, finfo->repository, ci->tag, message, - finfo->entries, finfo->rcs); + err = remove_file (finfo, ci->tag, message); #ifdef SERVER_SUPPORT if (server_active) { server_scratch_entry_only (); - server_updated (finfo->file, finfo->update_dir, finfo->repository, + server_updated (finfo, + NULL, + /* Doesn't matter, it won't get checked. */ - SERVER_UPDATED, (struct stat *) NULL, + SERVER_UPDATED, + + (struct stat *) NULL, (unsigned char *) NULL); } #endif @@ -979,6 +1230,33 @@ out: if (p) delnode (p); } + else + { + /* On success, retrieve the new version number of the file and + copy it into the log information (see logmsg.c + (logfile_write) for more details). We should only update + the version number for files that have been added or + modified but not removed. Why? classify_file_internal + will return the version number of a file even after it has + been removed from the archive, which is not the behavior we + want for our commitlog messages; we want the old version + number and then "NONE." */ + + if (ci->status != T_REMOVED) + { + p = findnode (ulist, finfo->file); + if (p) + { + Vers_TS *vers; + struct logfile_info *li; + + (void) classify_file_internal (finfo, &vers); + li = (struct logfile_info *) p->data; + li->rev_new = xstrdup (vers->vn_rcs); + freevers_ts (&vers); + } + } + } return (err); } @@ -988,12 +1266,13 @@ out: */ /* ARGSUSED */ static int -commit_filesdoneproc (err, repository, update_dir) +commit_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { - char *xtag = (char *) NULL; Node *p; List *ulist; @@ -1005,21 +1284,17 @@ commit_filesdoneproc (err, repository, update_dir) got_message = 0; - /* see if we need to specify a per-directory or -r option tag */ - if (tag == NULL) - ParseTag (&xtag, (char **) NULL); - Update_Logfile (repository, message, tag ? tag : xtag, (FILE *) 0, ulist); - if (xtag) - free (xtag); + Update_Logfile (repository, message, (FILE *) 0, ulist); /* Build the administrative files if necessary. */ { char *p; - if (strncmp (CVSroot, repository, strlen (CVSroot)) != 0) - error (0, 0, "internal error: repository doesn't begin with root"); - p = repository + strlen (CVSroot); + if (strncmp (CVSroot_directory, repository, + strlen (CVSroot_directory)) != 0) + error (0, 0, "internal error: repository (%s) doesn't begin with root (%s)", repository, CVSroot_directory); + p = repository + strlen (CVSroot_directory); if (*p == '/') ++p; if (strcmp ("CVSROOT", p) == 0) @@ -1042,7 +1317,7 @@ commit_filesdoneproc (err, repository, update_dir) { FILE *fp; - if ((fp = fopen (CVSADM_CIPROG, "r")) != NULL) + if ((fp = CVS_FOPEN (CVSADM_CIPROG, "r")) != NULL) { char *line; int line_length; @@ -1059,10 +1334,12 @@ commit_filesdoneproc (err, repository, update_dir) line[--line_length] = '\0'; repository = Name_Repository ((char *) NULL, update_dir); run_setup ("%s %s", line, repository); - (void) printf ("%s %s: Executing '", program_name, - command_name); + cvs_output (program_name, 0); + cvs_output (" ", 1); + cvs_output (command_name, 0); + cvs_output (": Executing '", 0); run_print (stdout); - (void) printf ("'\n"); + cvs_output ("'\n", 0); (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); free (repository); } @@ -1092,10 +1369,12 @@ commit_filesdoneproc (err, repository, update_dir) */ /* ARGSUSED */ static Dtype -commit_direntproc (dir, repos, update_dir) +commit_direntproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { Node *p; List *ulist; @@ -1117,13 +1396,12 @@ commit_direntproc (dir, repos, update_dir) error (0, 0, "Committing %s", update_dir); /* get commit message */ + real_repos = Name_Repository (dir, update_dir); + got_message = 1; if (use_editor) - { - got_message = 1; - real_repos = Name_Repository (dir, update_dir); do_editor (update_dir, &message, real_repos, ulist); - free (real_repos); - } + do_verify (message, real_repos); + free (real_repos); return (R_PROCESS); } @@ -1132,20 +1410,21 @@ commit_direntproc (dir, repos, update_dir) */ /* ARGSUSED */ static int -commit_dirleaveproc (dir, err, update_dir) +commit_dirleaveproc (callerdat, dir, err, update_dir, entries) + void *callerdat; char *dir; int err; char *update_dir; + List *entries; { /* update the per-directory tag info */ + /* FIXME? Why? The "commit examples" node of cvs.texinfo briefly + mentions commit -r being sticky, but apparently in the context of + this being a confusing feature! */ if (err == 0 && write_dirtag != NULL) { - WriteTag ((char *) NULL, write_dirtag, (char *) NULL); -#ifdef SERVER_SUPPORT - if (server_active) - server_set_sticky (update_dir, Name_Repository (dir, update_dir), - write_dirtag, (char *) NULL); -#endif + WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch, + update_dir, Name_Repository (dir, update_dir)); } return (err); @@ -1164,6 +1443,8 @@ findmaxrev (p, closure) Entnode *entdata; entdata = (Entnode *) p->data; + if (entdata->type != ENT_FILE) + return (0); cp = strchr (entdata->version, '.'); if (cp != NULL) *cp = '\0'; @@ -1182,17 +1463,13 @@ findmaxrev (p, closure) * link to keep it relative after we move it into the attic. */ static int -remove_file (file, repository, tag, message, entries, rcsnode) - char *file; - char *repository; +remove_file (finfo, tag, message) + struct file_info *finfo; char *tag; char *message; - List *entries; - RCSNode *rcsnode; { mode_t omask; int retcode; - char rcs[PATH_MAX]; char *tmp; int branch; @@ -1200,6 +1477,7 @@ remove_file (file, repository, tag, message, entries, rcsnode) char *corev; char *rev; char *prev_rev; + char *old_path; corev = NULL; rev = NULL; @@ -1207,20 +1485,22 @@ remove_file (file, repository, tag, message, entries, rcsnode) retcode = 0; - locate_rcs (file, repository, rcs); + if (finfo->rcs == NULL) + error (1, 0, "internal error: no parsed RCS file"); branch = 0; - if (tag && !(branch = RCS_isbranch (rcsnode, tag))) + if (tag && !(branch = RCS_isbranch (finfo->rcs, tag))) { /* a symbolic tag is specified; just remove the tag from the file */ - if ((retcode = RCS_deltag (rcs, tag, 1)) != 0) + if ((retcode = RCS_deltag (finfo->rcs, tag, 1)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, - "failed to remove tag `%s' from `%s'", tag, rcs); + "failed to remove tag `%s' from `%s'", tag, + finfo->fullname); return (1); } - Scratch_Entry (entries, file); + Scratch_Entry (finfo->entries, finfo->file); return (0); } @@ -1228,32 +1508,29 @@ remove_file (file, repository, tag, message, entries, rcsnode) /* commit a new, dead revision. */ /* Print message indicating that file is going to be removed. */ - (void) printf ("Removing %s;\n", file); + cvs_output ("Removing ", 0); + cvs_output (finfo->fullname, 0); + cvs_output (";\n", 0); rev = NULL; - lockflag = RCS_FLAGS_LOCK; + lockflag = 1; if (branch) { char *branchname; - rev = RCS_whatbranch (rcsnode, tag); + rev = RCS_whatbranch (finfo->rcs, tag); if (rev == NULL) { error (0, 0, "cannot find branch \"%s\".", tag); return (1); } - if (rcsnode == NULL) - { - error (0, 0, "boy, I'm confused."); - return (1); - } - branchname = RCS_getbranch (rcsnode, rev, 1); + branchname = RCS_getbranch (finfo->rcs, rev, 1); if (branchname == NULL) { /* no revision exists on this branch. use the previous revision but do not lock. */ - corev = RCS_gettag (rcsnode, tag, 1, 0); + corev = RCS_gettag (finfo->rcs, tag, 1, (int *) NULL); prev_rev = xstrdup(rev); lockflag = 0; } else @@ -1265,24 +1542,18 @@ remove_file (file, repository, tag, message, entries, rcsnode) } else /* Not a branch */ { - /* Get current head revision of file. */ - if (rcsnode == NULL) - { - error (0, 0, "could not find parsed rcsfile %s", file); - return (1); - } - prev_rev = RCS_head (rcsnode); + prev_rev = RCS_head (finfo->rcs); } /* if removing without a tag or a branch, then make sure the default branch is the trunk. */ if (!tag && !branch) { - if (RCS_setbranch (rcs, NULL) != 0) + if (RCS_setbranch (finfo->rcs, NULL) != 0) { error (0, 0, "cannot change branch to default for %s", - rcs); + finfo->fullname); return (1); } } @@ -1293,71 +1564,85 @@ remove_file (file, repository, tag, message, entries, rcsnode) temp directory which is the kludgy way in which server.c tells time_stamp that the file is no longer around. Remove it so we can create temp files with that name (ignore errors). */ - unlink_file (file); + unlink_file (finfo->file); } #endif /* check something out. Generally this is the head. If we have a - particular rev, then name it. except when creating a branch, - lock the rev we're checking out. */ - retcode = RCS_checkout (rcs, "", rev ? corev : NULL, NULL, RUN_TTY, - lockflag, 1); + particular rev, then name it. */ + retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL, + (char *) NULL, (char *) NULL, RUN_TTY, + (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, - "failed to check out `%s'", rcs); + "failed to check out `%s'", finfo->fullname); return (1); } + /* Except when we are creating a branch, lock the revision so that + we can check in the new revision. */ + if (lockflag) + RCS_lock (finfo->rcs, rev ? corev : NULL, 0); + if (corev != NULL) free (corev); - retcode = RCS_checkin (rcs, NULL, message, rev, RCS_FLAGS_DEAD, 1); + retcode = RCS_checkin (finfo->rcs->path, finfo->file, message, rev, + RCS_FLAGS_DEAD | RCS_FLAGS_QUIET); if (retcode != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, - "failed to commit dead revision for `%s'", rcs); + "failed to commit dead revision for `%s'", finfo->fullname); return (1); } if (rev != NULL) free (rev); + old_path = finfo->rcs->path; if (!branch) { /* this was the head; really move it into the Attic */ - tmp = xmalloc(strlen(repository) + + tmp = xmalloc(strlen(finfo->repository) + sizeof('/') + sizeof(CVSATTIC) + sizeof('/') + - strlen(file) + + strlen(finfo->file) + sizeof(RCSEXT) + 1); - (void) sprintf (tmp, "%s/%s", repository, CVSATTIC); + (void) sprintf (tmp, "%s/%s", finfo->repository, CVSATTIC); omask = umask (cvsumask); (void) CVS_MKDIR (tmp, 0777); (void) umask (omask); - (void) sprintf (tmp, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT); + (void) sprintf (tmp, "%s/%s/%s%s", finfo->repository, CVSATTIC, finfo->file, RCSEXT); - if (strcmp (rcs, tmp) != 0 - && rename (rcs, tmp) == -1 - && (isreadable (rcs) || !isreadable (tmp))) + if (strcmp (finfo->rcs->path, tmp) != 0 + && CVS_RENAME (finfo->rcs->path, tmp) == -1 + && (isreadable (finfo->rcs->path) || !isreadable (tmp))) { free(tmp); return (1); } - free(tmp); + /* The old value of finfo->rcs->path is in old_path, and is + freed below. */ + finfo->rcs->path = tmp; } /* Print message that file was removed. */ - (void) printf ("%s <-- %s\n", rcs, file); - (void) printf ("new revision: delete; "); - (void) printf ("previous revision: %s\n", prev_rev); - (void) printf ("done\n"); + cvs_output (old_path, 0); + cvs_output (" <-- ", 0); + cvs_output (finfo->file, 0); + cvs_output ("\nnew revision: delete; previous revision: ", 0); + cvs_output (prev_rev, 0); + cvs_output ("\ndone\n", 0); free(prev_rev); - Scratch_Entry (entries, file); + if (old_path != finfo->rcs->path) + free (old_path); + + Scratch_Entry (finfo->entries, finfo->file); return (0); } @@ -1365,31 +1650,30 @@ remove_file (file, repository, tag, message, entries, rcsnode) * Do the actual checkin for added files */ static int -finaladd (file, rev, tag, options, update_dir, repository, entries) - char *file; +finaladd (finfo, rev, tag, options) + struct file_info *finfo; char *rev; char *tag; char *options; - char *update_dir; - char *repository; - List *entries; { int ret; - char tmp[PATH_MAX]; - char rcs[PATH_MAX]; + char *rcs; - locate_rcs (file, repository, rcs); - ret = Checkin ('A', file, update_dir, repository, rcs, rev, tag, options, - message, entries); + rcs = locate_rcs (finfo->file, finfo->repository); + ret = Checkin ('A', finfo, rcs, rev, tag, options, message); if (ret == 0) { - (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_LOG); + char *tmp = xmalloc (strlen (finfo->file) + sizeof (CVSADM) + + sizeof (CVSEXT_LOG) + 10); + (void) sprintf (tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); (void) unlink_file (tmp); + free (tmp); } else - fixaddfile (file, repository); + fixaddfile (finfo->file, finfo->repository); (void) time (&last_register_time); + free (rcs); return (ret); } @@ -1398,18 +1682,14 @@ finaladd (file, rev, tag, options, update_dir, repository, entries) * Unlock an rcs file */ static void -unlockrcs (file, repository) - char *file; - char *repository; +unlockrcs (rcs) + RCSNode *rcs; { - char rcs[PATH_MAX]; - int retcode = 0; - - locate_rcs (file, repository, rcs); + int retcode; if ((retcode = RCS_unlock (rcs, NULL, 0)) != 0) error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, - "could not unlock %s", rcs); + "could not unlock %s", rcs->path); } /* @@ -1421,10 +1701,10 @@ fixaddfile (file, repository) char *repository; { RCSNode *rcsfile; - char rcs[PATH_MAX]; + char *rcs; int save_really_quiet; - locate_rcs (file, repository, rcs); + rcs = locate_rcs (file, repository); save_really_quiet = really_quiet; really_quiet = 1; if ((rcsfile = RCS_parsercsfile (rcs)) == NULL) @@ -1432,26 +1712,24 @@ fixaddfile (file, repository) else freercsnode (&rcsfile); really_quiet = save_really_quiet; + free (rcs); } /* * put the branch back on an rcs file */ static void -fixbranch (file, repository, branch) - char *file; - char *repository; +fixbranch (rcs, branch) + RCSNode *rcs; char *branch; { - char rcs[PATH_MAX]; - int retcode = 0; + int retcode; - if (branch != NULL && branch[0] != '\0') + if (branch != NULL) { - locate_rcs (file, repository, rcs); if ((retcode = RCS_setbranch (rcs, branch)) != 0) error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, - "cannot restore branch to %s for %s", branch, rcs); + "cannot restore branch to %s for %s", branch, rcs->path); } } @@ -1469,74 +1747,110 @@ checkaddfile (file, repository, tag, options, rcsnode) char *options; RCSNode **rcsnode; { - char rcs[PATH_MAX]; - char fname[PATH_MAX]; + char *rcs; + char *fname; mode_t omask; int retcode = 0; int newfile = 0; + RCSNode *rcsfile = NULL; + int retval; if (tag) { - (void) sprintf(rcs, "%s/%s", repository, CVSATTIC); - omask = umask (cvsumask); - if (CVS_MKDIR (rcs, 0777) != 0 && errno != EEXIST) - error (1, errno, "cannot make directory `%s'", rcs);; - (void) umask (omask); - (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT); + rcs = xmalloc (strlen (repository) + strlen (file) + + sizeof (RCSEXT) + sizeof (CVSATTIC) + 10); + (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); + if (! isreadable (rcs)) + { + (void) sprintf(rcs, "%s/%s", repository, CVSATTIC); + omask = umask (cvsumask); + if (CVS_MKDIR (rcs, 0777) != 0 && errno != EEXIST) + error (1, errno, "cannot make directory `%s'", rcs);; + (void) umask (omask); + (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, + RCSEXT); + } } else - locate_rcs (file, repository, rcs); + rcs = locate_rcs (file, repository); - if (isreadable(rcs)) + if (isreadable (rcs)) { /* file has existed in the past. Prepare to resurrect. */ - char oldfile[PATH_MAX]; char *rev; - RCSNode *rcsfile; + + if ((rcsfile = *rcsnode) == NULL) + { + error (0, 0, "could not find parsed rcsfile %s", file); + retval = 1; + goto out; + } if (tag == NULL) { + char *oldfile; + /* we are adding on the trunk, so move the file out of the Attic. */ - strcpy (oldfile, rcs); + oldfile = xstrdup (rcs); sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); - if (strcmp (oldfile, rcs) == 0 - || rename (oldfile, rcs) != 0 - || isreadable (oldfile) + if (strcmp (oldfile, rcs) == 0) + { + error (0, 0, "internal error: confused about attic for %s", + oldfile); + out1: + free (oldfile); + retval = 1; + goto out; + } + if (CVS_RENAME (oldfile, rcs) != 0) + { + error (0, errno, "failed to move `%s' out of the attic", + oldfile); + goto out1; + } + if (isreadable (oldfile) || !isreadable (rcs)) { - error (0, 0, "failed to move `%s' out of the attic.", - file); - return (1); + error (0, 0, "\ +internal error: `%s' didn't move out of the attic", + oldfile); + goto out1; } + free (oldfile); + free (rcsfile->path); + rcsfile->path = xstrdup (rcs); } - if ((rcsfile = *rcsnode) == NULL) - { - error (0, 0, "could not find parsed rcsfile %s", file); - return (1); - } - - rev = RCS_getversion (rcsfile, tag, NULL, 1, 0); + rev = RCS_getversion (rcsfile, tag, NULL, 1, (int *) NULL); /* and lock it */ - if (lock_RCS (file, rcs, rev, repository)) { + if (lock_RCS (file, rcsfile, rev, repository)) + { error (0, 0, "cannot lock `%s'.", rcs); - free (rev); - return (1); + if (rev != NULL) + free (rev); + retval = 1; + goto out; } - free (rev); - } else { + if (rev != NULL) + free (rev); + } + else + { /* this is the first time we have ever seen this file; create an rcs file. */ run_setup ("%s%s -x,v/ -i", Rcsbin, RCS); + fname = xmalloc (strlen (file) + sizeof (CVSADM) + + sizeof (CVSEXT_LOG) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG); /* If the file does not exist, no big deal. In particular, the server does not (yet at least) create CVSEXT_LOG files. */ if (isfile (fname)) run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG); + free (fname); /* Set RCS keyword expansion options. */ if (options && options[0] == '-' && options[1] == 'k') @@ -1546,7 +1860,8 @@ checkaddfile (file, repository, tag, options, rcsnode) { error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "could not create %s", rcs); - return (1); + retval = 1; + goto out; } newfile = 1; } @@ -1558,6 +1873,8 @@ checkaddfile (file, repository, tag, options, rcsnode) char *tmp; /* move the new file out of the way. */ + fname = xmalloc (strlen (file) + sizeof (CVSADM) + + sizeof (CVSPREFIX) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); rename_file (file, fname); copy_file (DEVNULL, file); @@ -1567,22 +1884,40 @@ checkaddfile (file, repository, tag, options, rcsnode) (void) sprintf (tmp, "file %s was initially added on branch %s.", file, tag); retcode = RCS_checkin (rcs, NULL, tmp, NULL, - RCS_FLAGS_DEAD | RCS_FLAGS_QUIET, 0); + RCS_FLAGS_DEAD | RCS_FLAGS_QUIET); free (tmp); if (retcode != 0) { error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "could not create initial dead revision %s", rcs); - return (1); + retval = 1; + goto out; } /* put the new file back where it was */ rename_file (fname, file); - + free (fname); + + assert (rcsfile == NULL); + rcsfile = RCS_parse (file, repository); + if (rcsfile == NULL) + { + error (0, 0, "could not read %s", rcs); + retval = 1; + goto out; + } + if (rcsnode != NULL) + { + assert (*rcsnode == NULL); + *rcsnode = rcsfile; + } + /* and lock it once again. */ - if (lock_RCS (file, rcs, NULL, repository)) { + if (lock_RCS (file, rcsfile, NULL, repository)) + { error (0, 0, "cannot lock `%s'.", rcs); - return (1); + retval = 1; + goto out; } } @@ -1590,78 +1925,71 @@ checkaddfile (file, repository, tag, options, rcsnode) { /* when adding with a tag, we need to stub a branch, if it doesn't already exist. */ - RCSNode *rcsfile; - rcsfile = RCS_parse (file, repository); if (rcsfile == NULL) { - error (0, 0, "could not read %s", rcs); - return (1); + if (rcsnode != NULL && *rcsnode != NULL) + rcsfile = *rcsnode; + else + { + rcsfile = RCS_parse (file, repository); + if (rcsfile == NULL) + { + error (0, 0, "could not read %s", rcs); + retval = 1; + goto out; + } + } } - - if (!RCS_nodeisbranch (rcsfile, tag)) { + + if (!RCS_nodeisbranch (rcsfile, tag)) + { /* branch does not exist. Stub it. */ char *head; char *magicrev; - - head = RCS_getversion (rcsfile, NULL, NULL, 0, 0); + + head = RCS_getversion (rcsfile, NULL, NULL, 0, (int *) NULL); magicrev = RCS_magicrev (rcsfile, head); - if ((retcode = RCS_settag(rcs, tag, magicrev)) != 0) + + retcode = RCS_settag (rcsfile, tag, magicrev); + + free (head); + free (magicrev); + + if (retcode != 0) { error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "could not stub branch %s for %s", tag, rcs); - return (1); - } - - freercsnode (&rcsfile); - - /* reparse the file, then add it to our list. */ - rcsfile = RCS_parse (file, repository); - if (rcsfile == NULL) - { - error (0, 0, "could not reparse %s", rcs); - return (1); + retval = 1; + goto out; } - - free (head); - free (magicrev); } else { /* lock the branch. (stubbed branches need not be locked.) */ - if (lock_RCS (file, rcs, NULL, repository)) { + if (lock_RCS (file, rcsfile, NULL, repository)) + { error (0, 0, "cannot lock `%s'.", rcs); - return (1); + retval = 1; + goto out; } } - if (rcsnode) - freercsnode(rcsnode); - *rcsnode = rcsfile; + if (rcsnode && *rcsnode != rcsfile) + { + freercsnode(rcsnode); + *rcsnode = rcsfile; + } } fileattr_newfile (file); fix_rcs_modes (rcs, file); - return (0); -} - -/* - * Lock the rcs file ``file'' - */ -static int -lockrcsfile (file, repository, rev) - char *file; - char *repository; - char *rev; -{ - char rcs[PATH_MAX]; + retval = 0; - locate_rcs (file, repository, rcs); - if (lock_RCS (file, rcs, rev, repository) != 0) - return (1); - else - return (0); + out: + free (rcs); + return retval; } /* @@ -1673,11 +2001,10 @@ lockrcsfile (file, repository, rev) static int lock_RCS (user, rcs, rev, repository) char *user; - char *rcs; + RCSNode *rcs; char *rev; char *repository; { - RCSNode *rcsfile; char *branch = NULL; int err = 0; @@ -1692,29 +2019,19 @@ lock_RCS (user, rcs, rev, repository) */ if (rev == NULL || (rev && isdigit (*rev) && numdots (rev) < 2)) { - if ((rcsfile = RCS_parsercsfile (rcs)) == NULL) - { - /* invalid rcs file? */ - err = 1; - } - else + branch = xstrdup (rcs->branch); + if (branch != NULL) { - /* rcsfile is valid */ - branch = xstrdup (rcsfile->branch); - freercsnode (&rcsfile); - if (branch != NULL) + if (RCS_setbranch (rcs, NULL) != 0) { - if (RCS_setbranch (rcs, NULL) != 0) - { - error (0, 0, "cannot change branch to default for %s", - rcs); - if (branch) - free (branch); - return (1); - } + error (0, 0, "cannot change branch to default for %s", + rcs->path); + if (branch) + free (branch); + return (1); } - err = RCS_lock(rcs, NULL, 0); } + err = RCS_lock(rcs, NULL, 0); } else { @@ -1723,52 +2040,96 @@ lock_RCS (user, rcs, rev, repository) if (err == 0) { + if (sbranch != NULL) + free (sbranch); if (branch) { - (void) strcpy (sbranch, branch); - free (branch); + sbranch = branch; } else - sbranch[0] = '\0'; + sbranch = NULL; return (0); } /* try to restore the branch if we can on error */ if (branch != NULL) - fixbranch (user, repository, branch); + fixbranch (rcs, branch); if (branch) free (branch); return (1); } -/* - * Called when "add"ing files to the RCS respository, as it is necessary to - * preserve the file modes in the same fashion that RCS does. This would be - * automatic except that we are placing the RCS ,v file very far away from - * the user file, and I can't seem to convince RCS of the location of the - * user file. So we munge it here, after the ,v file has been successfully - * initialized with "rcs -i". - */ +/* Called when "add"ing files to the RCS respository. It doesn't seem to + be possible to get RCS to use the right mode, so we change it after + the fact. */ + static void fix_rcs_modes (rcs, user) char *rcs; char *user; { struct stat sb; + mode_t rcs_mode; - if (stat (user, &sb) != -1) - (void) chmod (rcs, (int) sb.st_mode & ~0222); + if (CVS_STAT (user, &sb) < 0) + { + /* FIXME: Should be ->fullname. */ + error (0, errno, "warning: cannot stat %s", user); + return; + } + + /* Now we compute the new mode. + + The algorithm that we use is: + + Write permission is always off (this is what RCS and CVS have always + done). + + If S_IRUSR is on (user read), then the read permission of + the RCS file will be on. It would seem that if this is off, + then other users can't do "cvs update" and such, so perhaps this + should be hardcoded to being on (it is a strange case, though--the + case in which a user file doesn't have user read permission on). + + If S_IXUSR is on (user execute), then set execute permission + on the RCS file. This allows other users who check out the file + to get the right setting for whether a shell script (for example) + has the executable bit set. + + The result of that calculation is modified by CVSUMASK. The + reason, of course, that the read and execute settings take the + user bit and copy it to all three bits (user, group, other), is + that it should be CVSUMASK, not the umask of individual users, + which is the sole determiner of modes in the repository. */ + + rcs_mode = 0; + if (sb.st_mode & S_IRUSR) + rcs_mode |= S_IRUSR | S_IRGRP | S_IROTH; + if (sb.st_mode & S_IXUSR) + rcs_mode |= S_IXUSR | S_IXGRP | S_IXOTH; + rcs_mode &= ~cvsumask; + if (chmod (rcs, rcs_mode) < 0) + error (0, errno, "warning: cannot change mode of %s", rcs); } /* - * free an UPDATE node's data (really nothing to do) + * free an UPDATE node's data */ void update_delproc (p) Node *p; { - p->data = (char *) NULL; + struct logfile_info *li; + + li = (struct logfile_info *) p->data; + if (li->tag) + free (li->tag); + if (li->rev_old) + free (li->rev_old); + if (li->rev_new) + free (li->rev_new); + free (li); } /* @@ -1805,15 +2166,23 @@ masterlist_delproc (p) free (ml); } -/* - * Find an RCS file in the repository. - */ -static void -locate_rcs (file, repository, rcs) +/* Find an RCS file in the repository. Most parts of CVS will want to + rely instead on RCS_parse which performs a similar operation and is + called by recurse.c which then puts the result in useful places + like the rcs field of struct file_info. + + REPOSITORY is the repository (including the directory) and FILE is + the filename within that directory (without RCSEXT). Returns a + newly-malloc'd array containing the absolute pathname of the RCS + file that was found. */ +static char * +locate_rcs (file, repository) char *file; char *repository; - char *rcs; { + char *rcs; + + rcs = xmalloc (strlen (repository) + strlen (file) + sizeof (RCSEXT) + 10); (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); if (!isreadable (rcs)) { @@ -1821,4 +2190,5 @@ locate_rcs (file, repository, rcs) if (!isreadable (rcs)) (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); } + return rcs; } diff --git a/contrib/cvs/src/create_adm.c b/contrib/cvs/src/create_adm.c index fd7fd4d..0ef6e57 100644 --- a/contrib/cvs/src/create_adm.c +++ b/contrib/cvs/src/create_adm.c @@ -16,32 +16,34 @@ /* update_dir includes dir as its last component. */ void -Create_Admin (dir, update_dir, repository, tag, date) +Create_Admin (dir, update_dir, repository, tag, date, nonbranch) char *dir; char *update_dir; char *repository; char *tag; char *date; + int nonbranch; { FILE *fout; char *cp; - char tmp[PATH_MAX]; + char *tmp; #ifdef SERVER_SUPPORT if (trace) { - char wd[PATH_MAX]; - getwd (wd); + char *wd = xgetwd (); fprintf (stderr, "%c-> Create_Admin (%s, %s, %s, %s, %s) in %s\n", (server_active) ? 'S' : ' ', dir, update_dir, repository, tag ? tag : "", date ? date : "", wd); + free (wd); } #endif if (noexec) return; + tmp = xmalloc (strlen (dir) + 100); if (dir != NULL) (void) sprintf (tmp, "%s/%s", dir, CVSADM); else @@ -53,12 +55,12 @@ Create_Admin (dir, update_dir, repository, tag, date) /* record the current cvs root for later use */ - Create_Root (dir, CVSroot); + Create_Root (dir, CVSroot_original); if (dir != NULL) (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP); else (void) strcpy (tmp, CVSADM_REP); - fout = fopen (tmp, "w+"); + fout = CVS_FOPEN (tmp, "w+"); if (fout == NULL) { if (update_dir[0] == '\0') @@ -67,20 +69,21 @@ Create_Admin (dir, update_dir, repository, tag, date) error (1, errno, "cannot open %s/%s", update_dir, CVSADM_REP); } cp = repository; - strip_path (cp); + strip_trailing_slashes (cp); #ifdef RELATIVE_REPOS /* * If the Repository file is to hold a relative path, try to strip off * the leading CVSroot argument. */ - if (CVSroot != NULL) + if (CVSroot_directory != NULL) { - char path[PATH_MAX]; + char *path = xmalloc (strlen (CVSroot_directory) + 10); - (void) sprintf (path, "%s/", CVSroot); + (void) sprintf (path, "%s/", CVSroot_directory); if (strncmp (repository, path, strlen (path)) == 0) cp = repository + strlen (path); + free (path); } #endif @@ -104,7 +107,7 @@ Create_Admin (dir, update_dir, repository, tag, date) (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT); else (void) strcpy (tmp, CVSADM_ENT); - fout = fopen (tmp, "w+"); + fout = CVS_FOPEN (tmp, "w+"); if (fout == NULL) { if (update_dir[0] == '\0') @@ -121,12 +124,11 @@ Create_Admin (dir, update_dir, repository, tag, date) } /* Create a new CVS/Tag file */ - WriteTag (dir, tag, date); + WriteTag (dir, tag, date, nonbranch, update_dir, repository); #ifdef SERVER_SUPPORT if (server_active) { - server_set_sticky (update_dir, repository, tag, date); server_template (update_dir, repository); } @@ -137,4 +139,5 @@ Create_Admin (dir, update_dir, repository, tag, date) } #endif + free (tmp); } diff --git a/contrib/cvs/src/cvs.h b/contrib/cvs/src/cvs.h index 8e50f5d..81659f3 100644 --- a/contrib/cvs/src/cvs.h +++ b/contrib/cvs/src/cvs.h @@ -1,4 +1,10 @@ -/* $CVSid: @(#)cvs.h 1.86 94/10/22 $ */ +/* + * Copyright (c) 1992, Brian Berliner and Jeff Polk + * Copyright (c) 1989-1992, Brian Berliner + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS kit. + */ /* * basic information used in all source files @@ -76,7 +82,6 @@ extern int errno; #include "hash.h" #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) -#include "server.h" #include "client.h" #endif @@ -93,7 +98,10 @@ extern int errno; #include "rcs.h" -/* XXX - for now this is static */ +/* This actually gets set in system.h. Note that the _ONLY_ reason for + this is if various system calls (getwd, getcwd, readlink) require/want + us to use it. All other parts of CVS allocate pathname buffers + dynamically, and we want to keep it that way. */ #ifndef PATH_MAX #ifdef MAXPATHLEN #define PATH_MAX MAXPATHLEN+2 @@ -102,22 +110,8 @@ extern int errno; #endif #endif /* PATH_MAX */ -/* just in case this implementation does not define this */ -#ifndef L_tmpnam -#define L_tmpnam 50 -#endif - - -/* - * Copyright (c) 1992, Brian Berliner and Jeff Polk - * Copyright (c) 1989-1992, Brian Berliner - * - * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. - * - * Definitions for the CVS Administrative directory and the files it contains. - * Here as #define's to make changing the names a simple task. - */ +/* Definitions for the CVS Administrative directory and the files it contains. + Here as #define's to make changing the names a simple task. */ #ifdef USE_VMS_FILENAMES #define CVSADM "CVS" @@ -177,6 +171,7 @@ extern int errno; #define CVSROOTADM_COMMITINFO "commitinfo" #define CVSROOTADM_TAGINFO "taginfo" #define CVSROOTADM_EDITINFO "editinfo" +#define CVSROOTADM_VERIFYMSG "verifymsg" #define CVSROOTADM_HISTORY "history" #define CVSROOTADM_VALTAGS "val-tags" #define CVSROOTADM_IGNORE "cvsignore" @@ -184,6 +179,9 @@ extern int errno; #define CVSROOTADM_WRAPPER "cvswrappers" #define CVSROOTADM_NOTIFY "notify" #define CVSROOTADM_USERS "users" +#define CVSROOTADM_READERS "readers" +#define CVSROOTADM_WRITERS "writers" +#define CVSROOTADM_PASSWD "passwd" #define CVSNULLREPOS "Emptydir" /* an empty directory */ @@ -205,8 +203,29 @@ extern int errno; #define CVSDOTIGNORE ".cvsignore" #define CVSDOTWRAPPER ".cvswrappers" +/* Command attributes -- see function lookup_command_attribute(). */ +#define CVS_CMD_IGNORE_ADMROOT 1 + +/* Set if CVS does _not_ need to create a CVS/Root file upon + completion of this command. The name is confusing, both because + the meaning is closer to "does not use working directory" than + "uses working directory" and because the flag isn't really as + general purpose as it seems (cvs release sets it). */ + +#define CVS_CMD_USES_WORK_DIR 2 + +#define CVS_CMD_MODIFIES_REPOSITORY 4 + /* miscellaneous CVS defines */ + +/* This is the string which is at the start of the non-log-message lines + that we put up for the user when they edit the log message. */ #define CVSEDITPREFIX "CVS: " +/* Number of characters in CVSEDITPREFIX to compare when deciding to strip + off those lines. We don't check for the space, to accomodate users who + have editors which strip trailing spaces. */ +#define CVSEDITPREFIXLEN 4 + #define CVSLCKAGE (60*60) /* 1-hour old lock files cleaned up */ #define CVSLCKSLEEP 30 /* wait 30 seconds before retrying */ #define CVSBRANCH "1.1.1" /* RCS branch used for vendor srcs */ @@ -237,12 +256,15 @@ extern int errno; #define CVSREAD_DFLT FALSE /* writable files by default */ #define RCSBIN_ENV "RCSBIN" /* RCS binary directory */ -/* #define RCSBIN_DFLT Set by config.h */ +/* #define RCSBIN_DFLT Set by options.h */ + +#define TMPDIR_ENV "TMPDIR" /* Temporary directory */ +/* #define TMPDIR_DFLT Set by options.h */ #define EDITOR1_ENV "CVSEDITOR" /* which editor to use */ #define EDITOR2_ENV "VISUAL" /* which editor to use */ #define EDITOR3_ENV "EDITOR" /* which editor to use */ -/* #define EDITOR_DFLT Set by config.h */ +/* #define EDITOR_DFLT Set by options.h */ #define CVSROOT_ENV "CVSROOT" /* source directory root */ #define CVSROOT_DFLT NULL /* No dflt; must set for checkout */ @@ -251,7 +273,7 @@ extern int errno; #define WRAPPER_ENV "CVSWRAPPERS" /* name of the wrapper file */ #define CVSUMASK_ENV "CVSUMASK" /* Effective umask for repository */ -/* #define CVSUMASK_DFLT Set by config.h */ +/* #define CVSUMASK_DFLT Set by options.h */ /* * If the beginning of the Repository matches the following string, strip it @@ -261,20 +283,21 @@ extern int errno; */ #define REPOS_STRIP "/master/" -/* - * The maximum number of files per each CVS directory. This is mainly for - * sizing arrays statically rather than dynamically. 3000 seems plenty for - * now. - */ -#define MAXFILEPERDIR 3000 -#define MAXLINELEN 5000 /* max input line from a file */ -#define MAXPROGLEN 30000 /* max program length to system() */ -#define MAXLISTLEN 40000 /* For [A-Z]list holders */ -#define MAXDATELEN 50 /* max length for a date */ +/* Large enough to hold DATEFORM. Not an arbitrary limit as long as + it is used for that purpose, and not to hold a string from the + command line, the client, etc. */ +#define MAXDATELEN 50 + +/* The type of an entnode. */ +enum ent_type +{ + ENT_FILE, ENT_SUBDIR +}; /* structure of a entry record */ struct entnode { + enum ent_type type; char *user; char *version; char *timestamp; @@ -292,96 +315,22 @@ enum mtype }; /* - * defines for Classify_File() to determine the current state of a file. - * These are also used as types in the data field for the list we make for - * Update_Logfile in commit, import, and add. - */ -enum classify_type -{ - T_UNKNOWN = 1, /* no old-style analog existed */ - T_CONFLICT, /* C (conflict) list */ - T_NEEDS_MERGE, /* G (needs merging) list */ - T_MODIFIED, /* M (needs checked in) list */ - T_CHECKOUT, /* O (needs checkout) list */ - T_ADDED, /* A (added file) list */ - T_REMOVED, /* R (removed file) list */ - T_REMOVE_ENTRY, /* W (removed entry) list */ - T_UPTODATE, /* File is up-to-date */ -#ifdef SERVER_SUPPORT - T_PATCH, /* P Like C, but can patch */ -#endif - T_TITLE /* title for node type */ -}; -typedef enum classify_type Ctype; - -/* - * a struct vers_ts contains all the information about a file including the - * user and rcs file names, and the version checked out and the head. - * - * this is usually obtained from a call to Version_TS which takes a tag argument - * for the RCS file if desired - */ -struct vers_ts -{ - /* rcs version user file derives from, from CVS/Entries. - * it can have the following special values: - * empty = no user file - * 0 = user file is new - * -vers = user file to be removed. */ - char *vn_user; - - /* Numeric revision number corresponding to ->vn_tag (->vn_tag - will often be symbolic). */ - char *vn_rcs; - /* If ->tag corresponds to a tag which really exists in this file, - this is just a copy of ->tag. If not, this is either NULL or - the head revision. (Or something like that, see RCS_getversion - and friends). */ - char *vn_tag; - - /* This is the timestamp from stating the file in the working directory. - It is NULL if there is no file in the working directory. */ - char *ts_user; - /* Timestamp from CVS/Entries. For the server, ts_user and ts_rcs - are computed in a slightly different way, but the fact remains that - if they are equal the file in the working directory is unmodified - and if they differ it is modified. */ - char *ts_rcs; - - /* Options from CVS/Entries (keyword expansion). */ - char *options; - - /* If non-NULL, there was a conflict (or merely a merge? See merge_file) - and the time stamp in this field is the time stamp of the working - directory file which was created with the conflict markers in it. - This is from CVS/Entries. */ - char *ts_conflict; - - /* Tag specified on the command line, or if none, tag stored in - CVS/Entries. */ - char *tag; - /* Date specified on the command line, or if none, date stored in - CVS/Entries. */ - char *date; - - /* Pointer to entries file node */ - Entnode *entdata; - - /* Pointer to parsed src file info */ - RCSNode *srcfile; -}; -typedef struct vers_ts Vers_TS; - -/* * structure used for list-private storage by Entries_Open() and - * Version_TS(). + * Version_TS() and Find_Directories(). */ struct stickydirtag { + /* These fields pass sticky tag information from Entries_Open() to + Version_TS(). */ int aflag; char *tag; char *date; - char *options; + int nonbranch; + + /* This field is set by Entries_Open() if there was subdirectory + information; Find_Directories() uses it to see whether it needs + to scan the directory itself. */ + int subdirs; }; /* Flags for find_{names,dirs} routines */ @@ -397,11 +346,14 @@ enum direnter_type R_SKIP_DIRS, /* don't process sub-dirs */ R_SKIP_ALL /* don't process files or dirs */ }; +#ifdef ENUMS_CAN_BE_TROUBLE +typedef int Dtype; +#else typedef enum direnter_type Dtype; +#endif extern char *program_name, *program_path, *command_name; -extern char *Rcsbin, *Editor, *CVSroot; -extern char *CVSADM_Root; +extern char *Rcsbin, *Tmpdir, *Editor; extern int cvsadmin_root; extern char *CurDir; extern int really_quiet, quiet; @@ -409,32 +361,48 @@ extern int use_editor; extern int cvswrite; extern mode_t cvsumask; +/* Access method specified in CVSroot. */ +typedef enum { + local_method, server_method, pserver_method, kserver_method, ext_method +} CVSmethod; +extern char *method_names[]; /* change this in root.c if you change + the enum above */ + +extern char *CVSroot_original; /* the active, complete CVSroot string */ +extern int client_active; /* nonzero if we are doing remote access */ +extern CVSmethod CVSroot_method; /* one of the enum values above */ +extern char *CVSroot_username; /* the username or NULL if method == local */ +extern char *CVSroot_hostname; /* the hostname or NULL if method == local */ +extern char *CVSroot_directory; /* the directory name */ + extern int trace; /* Show all commands */ extern int noexec; /* Don't modify disk anywhere */ extern int logoff; /* Don't write history entry */ +#ifdef AUTH_SERVER_SUPPORT +extern char *Pserver_Repos; /* used to check that same repos is + transmitted in pserver auth and in + CVS protocol. */ +#endif /* AUTH_SERVER_SUPPORT */ + extern char hostname[]; /* Externs that are included directly in the CVS sources */ -int RCS_settag PROTO((const char *, const char *, const char *)); -int RCS_deltag PROTO((const char *, const char *, int)); -int RCS_setbranch PROTO((const char *, const char *)); -int RCS_lock PROTO((const char *, const char *, int)); -int RCS_unlock PROTO((const char *, const char *, int)); +int RCS_exec_settag PROTO((const char *, const char *, const char *)); +int RCS_exec_deltag PROTO((const char *, const char *, int)); +int RCS_exec_setbranch PROTO((const char *, const char *)); +int RCS_exec_lock PROTO((const char *, const char *, int)); +int RCS_exec_unlock PROTO((const char *, const char *, int)); int RCS_merge PROTO((const char *, const char *, const char *, const char *)); -int RCS_checkout PROTO ((char *rcsfile, char *workfile, char *tag, - char *options, - char *sout, int flags, int noerr)); /* Flags used by RCS_* functions. See the description of the individual functions for which flags mean what for each function. */ -#define RCS_FLAGS_LOCK 1 -#define RCS_FLAGS_FORCE 2 -#define RCS_FLAGS_DEAD 4 -#define RCS_FLAGS_QUIET 8 -#define RCS_FLAGS_MODTIME 16 +#define RCS_FLAGS_FORCE 1 +#define RCS_FLAGS_DEAD 2 +#define RCS_FLAGS_QUIET 4 +#define RCS_FLAGS_MODTIME 8 int RCS_checkin PROTO ((char *rcsfile, char *workfile, char *message, - char *rev, int flags, int noerr)); + char *rev, int flags)); @@ -442,30 +410,35 @@ int RCS_checkin PROTO ((char *rcsfile, char *workfile, char *message, DBM *open_module PROTO((void)); FILE *open_file PROTO((const char *, const char *)); -List *Find_Directories PROTO((char *repository, int which)); +List *Find_Directories PROTO((char *repository, int which, List *entries)); void Entries_Close PROTO((List *entries)); List *Entries_Open PROTO((int aflag)); +void Subdirs_Known PROTO((List *entries)); +void Subdir_Register PROTO((List *, const char *, const char *)); +void Subdir_Deregister PROTO((List *, const char *, const char *)); char *Make_Date PROTO((char *rawdate)); char *Name_Repository PROTO((char *dir, char *update_dir)); char *Name_Root PROTO((char *dir, char *update_dir)); +int parse_cvsroot PROTO((char *CVSroot)); +void set_local_cvsroot PROTO((char *dir)); void Create_Root PROTO((char *dir, char *rootdir)); int same_directories PROTO((char *dir1, char *dir2)); char *Short_Repository PROTO((char *repository)); char *gca PROTO((char *rev1, char *rev2)); char *getcaller PROTO((void)); char *time_stamp PROTO((char *file)); + char *xmalloc PROTO((size_t bytes)); void *xrealloc PROTO((void *ptr, size_t bytes)); +void expand_string PROTO ((char **, size_t *, size_t)); char *xstrdup PROTO((const char *str)); void strip_trailing_newlines PROTO((char *str)); -int No_Difference PROTO((char *file, Vers_TS * vers, List * entries, - char *repository, char *update_dir)); +int pathname_levels PROTO ((char *path)); + typedef int (*CALLPROC) PROTO((char *repository, char *value)); int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all)); -int Reader_Lock PROTO((char *xrepository)); typedef RETSIGTYPE (*SIGCLEANUPPROC) PROTO(()); int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup)); -int Writer_Lock PROTO((List * list)); int isdir PROTO((const char *file)); int isfile PROTO((const char *file)); int islink PROTO((const char *file)); @@ -475,6 +448,7 @@ int isaccessible PROTO((const char *file, const int mode)); int isabsolute PROTO((const char *filename)); char *last_component PROTO((char *path)); char *get_homedir PROTO ((void)); +char *cvs_temp_name PROTO ((void)); int numdots PROTO((const char *s)); int unlink_file PROTO((const char *f)); @@ -486,29 +460,31 @@ int yesno PROTO((void)); void *valloc PROTO((size_t bytes)); time_t get_date PROTO((char *date, struct timeb *now)); void Create_Admin PROTO((char *dir, char *update_dir, - char *repository, char *tag, char *date)); + char *repository, char *tag, char *date, + int nonbranch)); +/* Locking subsystem (implemented in lock.c). */ + +int Reader_Lock PROTO((char *xrepository)); void Lock_Cleanup PROTO((void)); /* Writelock an entire subtree, well the part specified by ARGC, ARGV, LOCAL, and AFLAG, anyway. */ void lock_tree_for_write PROTO ((int argc, char **argv, int local, int aflag)); -/* Remove locks set by lock_tree_for_write. Currently removes readlocks - too. */ -void lock_tree_cleanup PROTO ((void)); +/* See lock.c for description. */ +extern void lock_dir_for_write PROTO ((char *)); -void ParseTag PROTO((char **tagp, char **datep)); void Scratch_Entry PROTO((List * list, char *fname)); -void WriteTag PROTO((char *dir, char *tag, char *date)); +void ParseTag PROTO((char **tagp, char **datep, int *nonbranchp)); +void WriteTag PROTO ((char *dir, char *tag, char *date, int nonbranch, + char *update_dir, char *repository)); void cat_module PROTO((int status)); void check_entries PROTO((char *dir)); void close_module PROTO((DBM * db)); void copy_file PROTO((const char *from, const char *to)); -void (*error_set_cleanup PROTO((void (*) (void)))) PROTO ((void)); void fperror PROTO((FILE * fp, int status, int errnum, char *message,...)); void free_names PROTO((int *pargc, char *argv[])); -void freevers_ts PROTO((Vers_TS ** versp)); extern int ign_name PROTO ((char *name)); void ign_add PROTO((char *ign, int hold)); @@ -517,15 +493,16 @@ void ign_setup PROTO((void)); void ign_dir_add PROTO((char *name)); int ignore_directory PROTO((char *name)); typedef void (*Ignore_proc) PROTO ((char *, char *)); -extern void ignore_files PROTO ((List *, char *, Ignore_proc)); +extern void ignore_files PROTO ((List *, List *, char *, Ignore_proc)); extern int ign_inhibit_server; extern int ign_case; #include "update.h" -void line2argv PROTO((int *pargc, char *argv[], char *line)); +void line2argv PROTO ((int *pargc, char ***argv, char *line)); void make_directories PROTO((const char *name)); void make_directory PROTO((const char *name)); +extern int mkdir_if_needed PROTO ((char *name)); void rename_file PROTO((const char *from, const char *to)); /* Expand wildcards in each element of (ARGC,ARGV). This is according to the files which exist in the current directory, and accordingly to OS-specific @@ -537,31 +514,27 @@ void rename_file PROTO((const char *from, const char *to)); extern void expand_wild PROTO ((int argc, char **argv, int *pargc, char ***pargv)); -void strip_path PROTO((char *path)); +#ifdef SERVER_SUPPORT +extern int cvs_casecmp PROTO ((char *, char *)); +extern int fopen_case PROTO ((char *, char *, FILE **, char **)); +#endif + void strip_trailing_slashes PROTO((char *path)); void update_delproc PROTO((Node * p)); void usage PROTO((const char *const *cpp)); void xchmod PROTO((char *fname, int writable)); char *xgetwd PROTO((void)); -int Checkin PROTO((int type, char *file, char *update_dir, - char *repository, char *rcs, char *rev, - char *tag, char *options, char *message, List *entries)); -Ctype Classify_File PROTO((char *file, char *tag, char *date, char *options, - int force_tag_match, int aflag, char *repository, - List *entries, RCSNode *rcsnode, Vers_TS **versp, - char *update_dir, int pipeout)); List *Find_Names PROTO((char *repository, int which, int aflag, List ** optentries)); void Register PROTO((List * list, char *fname, char *vn, char *ts, char *options, char *tag, char *date, char *ts_conflict)); -void Update_Logfile PROTO((char *repository, char *xmessage, char *xrevision, - FILE * xlogfp, List * xchanges)); -Vers_TS *Version_TS PROTO((char *repository, char *options, char *tag, - char *date, char *user, int force_tag_match, - int set_time, List * entries, RCSNode * rcs)); +void Update_Logfile PROTO((char *repository, char *xmessage, FILE * xlogfp, + List * xchanges)); void do_editor PROTO((char *dir, char **messagep, char *repository, List * changes)); +void do_verify PROTO((char *message, char *repository)); + typedef int (*CALLBACKPROC) PROTO((int *pargc, char *argv[], char *where, char *mwhere, char *mfile, int horten, int local_specified, char *omodule, char *msg)); @@ -595,10 +568,15 @@ struct file_info RCSNode *rcs; }; -typedef int (*FILEPROC) PROTO((struct file_info *finfo)); -typedef int (*FILESDONEPROC) PROTO((int err, char *repository, char *update_dir)); -typedef Dtype (*DIRENTPROC) PROTO((char *dir, char *repos, char *update_dir)); -typedef int (*DIRLEAVEPROC) PROTO((char *dir, int err, char *update_dir)); +typedef int (*FILEPROC) PROTO ((void *callerdat, struct file_info *finfo)); +typedef int (*FILESDONEPROC) PROTO ((void *callerdat, int err, + char *repository, char *update_dir, + List *entries)); +typedef Dtype (*DIRENTPROC) PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +typedef int (*DIRLEAVEPROC) PROTO ((void *callerdat, char *dir, int err, + char *update_dir, List *entries)); extern int mkmodules PROTO ((char *dir)); extern int init PROTO ((int argc, char **argv)); @@ -606,22 +584,20 @@ extern int init PROTO ((int argc, char **argv)); int do_module PROTO((DBM * db, char *mname, enum mtype m_type, char *msg, CALLBACKPROC callback_proc, char *where, int shorten, int local_specified, int run_module_prog, char *extra_arg)); -int do_recursion PROTO((FILEPROC xfileproc, FILESDONEPROC xfilesdoneproc, - DIRENTPROC xdirentproc, DIRLEAVEPROC xdirleaveproc, - Dtype xflags, int xwhich, int xaflag, int xreadlock, - int xdosrcs)); void history_write PROTO((int type, char *update_dir, char *revs, char *name, char *repository)); int start_recursion PROTO((FILEPROC fileproc, FILESDONEPROC filesdoneproc, DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc, + void *callerdat, int argc, char *argv[], int local, int which, int aflag, int readlock, char *update_preload, - int dosrcs, int wd_is_repos)); + int dosrcs)); void SIG_beginCrSect PROTO((void)); void SIG_endCrSect PROTO((void)); void read_cvsrc PROTO((int *argc, char ***argv, char *cmdname)); char *make_message_rcslegal PROTO((char *message)); +extern int file_has_markers PROTO ((struct file_info *)); /* flags for run_exec(), the fast system() for CVS */ #define RUN_NORMAL 0x0000 /* no special behaviour */ @@ -651,18 +627,142 @@ int filter_stream_through_program PROTO((int, int, char **, pid_t *)); pid_t waitpid PROTO((pid_t, int *, int)); +/* + * a struct vers_ts contains all the information about a file including the + * user and rcs file names, and the version checked out and the head. + * + * this is usually obtained from a call to Version_TS which takes a + * tag argument for the RCS file if desired + */ +struct vers_ts +{ + /* rcs version user file derives from, from CVS/Entries. + * it can have the following special values: + * empty = no user file + * 0 = user file is new + * -vers = user file to be removed. */ + char *vn_user; + + /* Numeric revision number corresponding to ->vn_tag (->vn_tag + will often be symbolic). */ + char *vn_rcs; + /* If ->tag is a simple tag in the RCS file--a tag which really + exists which is not a magic revision--and if ->date is NULL, + then this is a copy of ->tag. Otherwise, it is a copy of + ->vn_rcs. */ + char *vn_tag; + + /* This is the timestamp from stating the file in the working directory. + It is NULL if there is no file in the working directory. It is + "Is-modified" if we know the file is modified but don't have its + contents. */ + char *ts_user; + /* Timestamp from CVS/Entries. For the server, ts_user and ts_rcs + are computed in a slightly different way, but the fact remains that + if they are equal the file in the working directory is unmodified + and if they differ it is modified. */ + char *ts_rcs; + + /* Options from CVS/Entries (keyword expansion). */ + char *options; + + /* If non-NULL, there was a conflict (or merely a merge? See merge_file) + and the time stamp in this field is the time stamp of the working + directory file which was created with the conflict markers in it. + This is from CVS/Entries. */ + char *ts_conflict; + + /* Tag specified on the command line, or if none, tag stored in + CVS/Entries. */ + char *tag; + /* Date specified on the command line, or if none, date stored in + CVS/Entries. */ + char *date; + /* If this is 1, then tag is not a branch tag. If this is 0, then + tag may or may not be a branch tag. */ + int nonbranch; + + /* Pointer to entries file node */ + Entnode *entdata; + + /* Pointer to parsed src file info */ + RCSNode *srcfile; +}; +typedef struct vers_ts Vers_TS; + +Vers_TS *Version_TS PROTO ((struct file_info *finfo, char *options, char *tag, + char *date, int force_tag_match, + int set_time)); +void freevers_ts PROTO ((Vers_TS ** versp)); + +/* Miscellaneous CVS infrastructure which layers on top of the recursion + processor (for example, needs struct file_info). */ + +int Checkin PROTO ((int type, struct file_info *finfo, char *rcs, char *rev, + char *tag, char *options, char *message)); +int No_Difference PROTO ((struct file_info *finfo, Vers_TS *vers)); + +/* + * defines for Classify_File() to determine the current state of a file. + * These are also used as types in the data field for the list we make for + * Update_Logfile in commit, import, and add. + */ +enum classify_type +{ + T_UNKNOWN = 1, /* no old-style analog existed */ + T_CONFLICT, /* C (conflict) list */ + T_NEEDS_MERGE, /* G (needs merging) list */ + T_MODIFIED, /* M (needs checked in) list */ + T_CHECKOUT, /* O (needs checkout) list */ + T_ADDED, /* A (added file) list */ + T_REMOVED, /* R (removed file) list */ + T_REMOVE_ENTRY, /* W (removed entry) list */ + T_UPTODATE, /* File is up-to-date */ +#ifdef SERVER_SUPPORT + T_PATCH, /* P Like C, but can patch */ +#endif + T_TITLE /* title for node type */ +}; +typedef enum classify_type Ctype; + +Ctype Classify_File PROTO + ((struct file_info *finfo, char *tag, char *date, char *options, + int force_tag_match, int aflag, Vers_TS **versp, int pipeout)); + +/* + * structure used for list nodes passed to Update_Logfile() and + * do_editor(). + */ +struct logfile_info +{ + enum classify_type type; + char *tag; + char *rev_old; /* rev number before a commit/modify, + NULL for add or import */ + char *rev_new; /* rev number after a commit/modify, + add, or import, NULL for remove */ +}; + /* Wrappers. */ typedef enum { WRAP_MERGE, WRAP_COPY } WrapMergeMethod; -typedef enum { WRAP_TOCVS, WRAP_FROMCVS, WRAP_CONFLICT } WrapMergeHas; +typedef enum { + /* -t and -f wrapper options. Treating directories as single files. */ + WRAP_TOCVS, + WRAP_FROMCVS, + /* -k wrapper option. Default keyword expansion options. */ + WRAP_RCSOPTION +} WrapMergeHas; void wrap_setup PROTO((void)); int wrap_name_has PROTO((const char *name,WrapMergeHas has)); +char *wrap_rcsoption PROTO ((const char *fileName, int asFlag)); char *wrap_tocvs_process_file PROTO((const char *fileName)); int wrap_merge_is_copy PROTO((const char *fileName)); -char *wrap_fromcvs_process_file PROTO((const char *fileName)); +void wrap_fromcvs_process_file PROTO ((const char *fileName)); void wrap_add_file PROTO((const char *file,int temp)); void wrap_add PROTO((char *line,int temp)); +void wrap_send PROTO ((void)); /* Pathname expansion */ char *expand_path PROTO((char *name, char *file, int line)); @@ -678,13 +778,45 @@ int unedit PROTO ((int argc, char **argv)); int editors PROTO ((int argc, char **argv)); int watchers PROTO ((int argc, char **argv)); extern int annotate PROTO ((int argc, char **argv)); +extern int add PROTO ((int argc, char **argv)); +extern int admin PROTO ((int argc, char **argv)); +extern int checkout PROTO ((int argc, char **argv)); +extern int commit PROTO ((int argc, char **argv)); +extern int diff PROTO ((int argc, char **argv)); +extern int history PROTO ((int argc, char **argv)); +extern int import PROTO ((int argc, char **argv)); +extern int cvslog PROTO ((int argc, char **argv)); +#ifdef AUTH_CLIENT_SUPPORT +extern int login PROTO((int argc, char **argv)); +int logout PROTO((int argc, char **argv)); +#endif /* AUTH_CLIENT_SUPPORT */ +extern int patch PROTO((int argc, char **argv)); +extern int release PROTO((int argc, char **argv)); +extern int cvsremove PROTO((int argc, char **argv)); +extern int rtag PROTO((int argc, char **argv)); +extern int status PROTO((int argc, char **argv)); +extern int cvstag PROTO((int argc, char **argv)); + +extern unsigned long int lookup_command_attribute PROTO((char *)); #if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT) char *scramble PROTO ((char *str)); char *descramble PROTO ((char *str)); #endif /* AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT */ +#ifdef AUTH_CLIENT_SUPPORT +char *get_cvs_password PROTO((void)); +#endif /* AUTH_CLIENT_SUPPORT */ + extern void tag_check_valid PROTO ((char *, int, char **, int, int, char *)); +extern void tag_check_valid_join PROTO ((char *, int, char **, int, int, + char *)); + +extern void cvs_output PROTO ((const char *, size_t)); +extern void cvs_outerr PROTO ((const char *, size_t)); +extern void cvs_flusherr PROTO ((void)); +extern void cvs_flushout PROTO ((void)); -extern void cvs_output PROTO ((char *, size_t)); -extern void cvs_outerr PROTO ((char *, size_t)); +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) +#include "server.h" +#endif diff --git a/contrib/cvs/src/cvsbug.sh b/contrib/cvs/src/cvsbug.sh index ab26cfc..fd6ae7c 100755 --- a/contrib/cvs/src/cvsbug.sh +++ b/contrib/cvs/src/cvsbug.sh @@ -7,8 +7,6 @@ # This file is part of GNU GNATS. # Modified by Berliner for CVS. # -#ident "@(#)cvs/src:$Name: $:$Id: cvsbug.sh,v 1.10 1995/11/15 00:18:00 woods Exp $" -# # GNU GNATS 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) @@ -18,10 +16,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU GNATS; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # The version of this send-pr. VERSION=3.2 @@ -237,11 +231,14 @@ fi case "$FORMAT" in lisp) echo "$CATEGORIES" | \ - awk 'BEGIN {printf "( "} {printf "(\"%s\") ",$0} END {printf ")\n"}' + awk 'BEGIN {printf "( "} + {printf "(\"%s\") ",$0} + END {printf ")\n"}' exit 0 ;; norm) l=`echo "$CATEGORIES" | \ - awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } } + awk 'BEGIN {max = 0; } + { if (length($0) > max) { max = length($0); } } END {print max + 1;}'` c=`expr 70 / $l` if [ $c -eq 0 ]; then c=1; fi @@ -316,7 +313,8 @@ __EOF__ # Format the categories so they fit onto lines. l=`echo "$CATEGORIES" | \ - awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } } + awk 'BEGIN {max = 0; } + { if (length($0) > max) { max = length($0); } } END {print max + 1;}'` c=`expr 61 / $l` if [ $c -eq 0 ]; then c=1; fi diff --git a/contrib/cvs/src/cvsrc.c b/contrib/cvs/src/cvsrc.c index 140ce1c..819013c 100644 --- a/contrib/cvs/src/cvsrc.c +++ b/contrib/cvs/src/cvsrc.c @@ -55,13 +55,6 @@ read_cvsrc (argc, argv, cmdname) if (*argc == -1) return; - /* setup the new options list */ - - new_argc = 1; - max_new_argv = (*argc) + GROW; - new_argv = (char **) xmalloc (max_new_argv * sizeof (char*)); - new_argv[0] = xstrdup ((*argv)[0]); - /* determine filename for ~/.cvsrc */ homedir = get_homedir (); @@ -105,30 +98,28 @@ read_cvsrc (argc, argv, cmdname) fclose (cvsrcfile); + /* setup the new options list */ + + new_argc = 1; + max_new_argv = (*argc) + GROW; + new_argv = (char **) xmalloc (max_new_argv * sizeof (char*)); + new_argv[0] = xstrdup ((*argv)[0]); + if (found) { /* skip over command in the options line */ - optstart = strtok (line + command_len, "\t \n"); - - do + for (optstart = strtok (line + command_len, "\t \n"); + optstart; + optstart = strtok (NULL, "\t \n")) { - new_argv [new_argc] = xstrdup (optstart); - new_argv [new_argc+1] = NULL; - new_argc += 1; + new_argv [new_argc++] = xstrdup (optstart); if (new_argc >= max_new_argv) { - char **tmp_argv; max_new_argv += GROW; - tmp_argv = (char **) xmalloc (max_new_argv * sizeof (char*)); - for (i = 0; i <= new_argc; i++) - tmp_argv[i] = new_argv[i]; - free(new_argv); - new_argv = tmp_argv; + new_argv = (char **) xrealloc (new_argv, max_new_argv * sizeof (char*)); } - } - while ((optstart = strtok (NULL, "\t \n")) != NULL); } if (line != NULL) @@ -136,10 +127,14 @@ read_cvsrc (argc, argv, cmdname) /* now copy the remaining arguments */ + if (new_argc + *argc > max_new_argv) + { + max_new_argv = new_argc + *argc; + new_argv = (char **) xrealloc (new_argv, max_new_argv * sizeof (char*)); + } for (i=1; i < *argc; i++) { - new_argv [new_argc] = (*argv)[i]; - new_argc += 1; + new_argv [new_argc++] = (*argv)[i]; } *argc = new_argc; diff --git a/contrib/cvs/src/edit.c b/contrib/cvs/src/edit.c index 0473a03..8d3bbb1 100644 --- a/contrib/cvs/src/edit.c +++ b/contrib/cvs/src/edit.c @@ -8,11 +8,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #include "cvs.h" #include "getline.h" @@ -29,23 +25,26 @@ static int setting_tedit; static int setting_tunedit; static int setting_tcommit; -static int onoff_fileproc PROTO ((struct file_info *finfo)); +static int onoff_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -onoff_fileproc (finfo) +onoff_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { fileattr_set (finfo->file, "_watched", turning_on ? "" : NULL); return 0; } -static int onoff_filesdoneproc PROTO ((int, char *, char *)); +static int onoff_filesdoneproc PROTO ((void *, int, char *, char *, List *)); static int -onoff_filesdoneproc (err, repository, update_dir) +onoff_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { if (setting_default) fileattr_set (NULL, "_watched", turning_on ? "" : NULL); @@ -62,13 +61,16 @@ watch_onoff (argc, argv) int err; optind = 1; - while ((c = getopt (argc, argv, "l")) != -1) + while ((c = getopt (argc, argv, "+lR")) != -1) { switch (c) { case 'l': local = 1; break; + case 'R': + local = 0; + break; case '?': default: usage (watch_usage); @@ -88,10 +90,7 @@ watch_onoff (argc, argv) if (local) send_arg ("-l"); send_file_names (argc, argv, SEND_EXPAND_WILD); - /* FIXME: We shouldn't have to send current files, but I'm not sure - whether it works. So send the files -- - it's slower but it works. */ - send_files (argc, argv, local, 0); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); send_to_server (turning_on ? "watch-on\012" : "watch-off\012", 0); return get_responses_and_close (); } @@ -102,11 +101,11 @@ watch_onoff (argc, argv) lock_tree_for_write (argc, argv, local, 0); err = start_recursion (onoff_fileproc, onoff_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); - lock_tree_cleanup (); + Lock_Cleanup (); return err; } @@ -128,10 +127,11 @@ watch_off (argc, argv) return watch_onoff (argc, argv); } -static int dummy_fileproc PROTO ((struct file_info *finfo)); +static int dummy_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -dummy_fileproc (finfo) +dummy_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { /* This is a pretty hideous hack, but the gist of it is that recurse.c @@ -140,7 +140,7 @@ dummy_fileproc (finfo) return 0; } -static int ncheck_fileproc PROTO ((struct file_info *finfo)); +static int ncheck_fileproc PROTO ((void *callerdat, struct file_info *finfo)); /* Check for and process notifications. Local only. I think that doing this as a fileproc is the only way to catch all the @@ -149,7 +149,8 @@ static int ncheck_fileproc PROTO ((struct file_info *finfo)); processed the directory. */ static int -ncheck_fileproc (finfo) +ncheck_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { int notif_type; @@ -165,7 +166,7 @@ ncheck_fileproc (finfo) /* We send notifications even if noexec. I'm not sure which behavior is most sensible. */ - fp = fopen (CVSADM_NOTIFY, "r"); + fp = CVS_FOPEN (CVSADM_NOTIFY, "r"); if (fp == NULL) { if (!existence_error (errno)) @@ -212,7 +213,7 @@ ncheck_fileproc (finfo) if (fclose (fp) < 0) error (0, errno, "cannot close %s", CVSADM_NOTIFY); - if (unlink (CVSADM_NOTIFY) < 0) + if ( CVS_UNLINK (CVSADM_NOTIFY) < 0) error (0, errno, "cannot remove %s", CVSADM_NOTIFY); return 0; @@ -243,9 +244,9 @@ send_notifications (argc, argv, local) } err += start_recursion (dummy_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); send_to_server ("noop\012", 0); if (strcmp (command_name, "release") == 0) @@ -260,18 +261,19 @@ send_notifications (argc, argv, local) lock_tree_for_write (argc, argv, local, 0); err += start_recursion (ncheck_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); - lock_tree_cleanup (); + 0); + Lock_Cleanup (); } return err; } -static int edit_fileproc PROTO ((struct file_info *finfo)); +static int edit_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -edit_fileproc (finfo) +edit_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { FILE *fp; @@ -313,19 +315,10 @@ edit_fileproc (finfo) copy so that if the user removes the working file, then restores it with "cvs update" (which clears _editors but does not update CVSADM_BASE), then a future "cvs edit" can still win. */ - /* Could save a system call by only calling mkdir if trying to create - the output file fails. But copy_file isn't set up to facilitate - that. */ - if (CVS_MKDIR (CVSADM_BASE, 0777) < 0) - { - if (errno != EEXIST -#ifdef EACCESS - /* OS/2; see longer comment in client.c. */ - && errno != EACCESS -#endif - ) - error (1, errno, "cannot mkdir %s", CVSADM_BASE); - } + /* Could save a system call by only calling mkdir_if_needed if + trying to create the output file fails. But copy_file isn't + set up to facilitate that. */ + mkdir_if_needed (CVSADM_BASE); basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (finfo->file)); strcpy (basefilename, CVSADM_BASE); strcat (basefilename, "/"); @@ -338,10 +331,11 @@ edit_fileproc (finfo) static const char *const edit_usage[] = { - "Usage: %s %s [-l] [files...]\n", + "Usage: %s %s [-lR] [files...]\n", "-l: Local directory only, not recursive\n", + "-R: Process directories recursively\n", "-a: Specify what actions for temporary watch, one of\n", - " edit,unedit,commit.all,none\n", + " edit,unedit,commit,all,none\n", NULL }; @@ -363,13 +357,16 @@ edit (argc, argv) setting_tunedit = 0; setting_tcommit = 0; optind = 1; - while ((c = getopt (argc, argv, "la:")) != -1) + while ((c = getopt (argc, argv, "+lRa:")) != -1) { switch (c) { case 'l': local = 1; break; + case 'R': + local = 0; + break; case 'a': a_omitted = 0; if (strcmp (optarg, "edit") == 0) @@ -412,19 +409,20 @@ edit (argc, argv) /* No need to readlock since we aren't doing anything to the repository. */ err = start_recursion (edit_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); err += send_notifications (argc, argv, local); return err; } -static int unedit_fileproc PROTO ((struct file_info *finfo)); +static int unedit_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -unedit_fileproc (finfo) +unedit_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { FILE *fp; @@ -494,13 +492,16 @@ unedit (argc, argv) usage (edit_usage); optind = 1; - while ((c = getopt (argc, argv, "l")) != -1) + while ((c = getopt (argc, argv, "+lR")) != -1) { switch (c) { case 'l': local = 1; break; + case 'R': + local = 0; + break; case '?': default: usage (edit_usage); @@ -513,9 +514,9 @@ unedit (argc, argv) /* No need to readlock since we aren't doing anything to the repository. */ err = start_recursion (unedit_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); err += send_notifications (argc, argv, local); @@ -768,16 +769,16 @@ notify_do (type, filename, who, val, watches, repository) size_t line_len = 0; args.notifyee = NULL; - usersname = xmalloc (strlen (CVSroot) + usersname = xmalloc (strlen (CVSroot_directory) + sizeof CVSROOTADM + sizeof CVSROOTADM_USERS + 20); - strcpy (usersname, CVSroot); + strcpy (usersname, CVSroot_directory); strcat (usersname, "/"); strcat (usersname, CVSROOTADM); strcat (usersname, "/"); strcat (usersname, CVSROOTADM_USERS); - fp = fopen (usersname, "r"); + fp = CVS_FOPEN (usersname, "r"); if (fp == NULL && !existence_error (errno)) error (0, errno, "cannot read %s", usersname); if (fp != NULL) @@ -871,7 +872,7 @@ notify_check (repository, update_dir) /* We send notifications even if noexec. I'm not sure which behavior is most sensible. */ - fp = fopen (CVSADM_NOTIFY, "r"); + fp = CVS_FOPEN (CVSADM_NOTIFY, "r"); if (fp == NULL) { if (!existence_error (errno)) @@ -897,7 +898,8 @@ notify_check (repository, update_dir) client_notify (repository, update_dir, filename, notif_type, val); } - + if (line) + free (line); if (ferror (fp)) error (0, errno, "cannot read %s", CVSADM_NOTIFY); if (fclose (fp) < 0) @@ -911,14 +913,17 @@ notify_check (repository, update_dir) static const char *const editors_usage[] = { - "Usage: %s %s [files...]\n", + "Usage: %s %s [-lR] [files...]\n", + "\t-l\tProcess this directory only (not recursive).\n", + "\t-R\tProcess directories recursively.\n", NULL }; -static int editors_fileproc PROTO ((struct file_info *finfo)); +static int editors_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -editors_fileproc (finfo) +editors_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *them; @@ -979,13 +984,16 @@ editors (argc, argv) usage (editors_usage); optind = 1; - while ((c = getopt (argc, argv, "l")) != -1) + while ((c = getopt (argc, argv, "+lR")) != -1) { switch (c) { case 'l': local = 1; break; + case 'R': + local = 0; + break; case '?': default: usage (editors_usage); @@ -1004,17 +1012,14 @@ editors (argc, argv) if (local) send_arg ("-l"); send_file_names (argc, argv, SEND_EXPAND_WILD); - /* FIXME: We shouldn't have to send current files, but I'm not sure - whether it works. So send the files -- - it's slower but it works. */ - send_files (argc, argv, local, 0); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); send_to_server ("editors\012", 0); return get_responses_and_close (); } #endif /* CLIENT_SUPPORT */ return start_recursion (editors_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, - 0, 0); + 0); } diff --git a/contrib/cvs/src/edit.h b/contrib/cvs/src/edit.h index 0a823ad..7c274a6 100644 --- a/contrib/cvs/src/edit.h +++ b/contrib/cvs/src/edit.h @@ -8,11 +8,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ extern int watch_on PROTO ((int argc, char **argv)); extern int watch_off PROTO ((int argc, char **argv)); diff --git a/contrib/cvs/src/entries.c b/contrib/cvs/src/entries.c index 350f7f8..03e29de 100644 --- a/contrib/cvs/src/entries.c +++ b/contrib/cvs/src/entries.c @@ -16,17 +16,25 @@ static Node *AddEntryNode PROTO((List * list, Entnode *entnode)); -static Entnode *fgetentent PROTO((FILE *)); +static Entnode *fgetentent PROTO((FILE *, char *, int *)); static int fputentent PROTO((FILE *, Entnode *)); +static Entnode *subdir_record PROTO((int, const char *, const char *)); + static FILE *entfile; static char *entfilename; /* for error messages */ /* * Construct an Entnode */ -Entnode * -Entnode_Create(user, vn, ts, options, tag, date, ts_conflict) +static Entnode *Entnode_Create PROTO ((enum ent_type, const char *, + const char *, const char *, + const char *, const char *, + const char *, const char *)); + +static Entnode * +Entnode_Create(type, user, vn, ts, options, tag, date, ts_conflict) + enum ent_type type; const char *user; const char *vn; const char *ts; @@ -39,6 +47,7 @@ Entnode_Create(user, vn, ts, options, tag, date, ts_conflict) /* Note that timestamp and options must be non-NULL */ ent = (Entnode *) xmalloc (sizeof (Entnode)); + ent->type = type; ent->user = xstrdup (user); ent->version = xstrdup (vn); ent->timestamp = xstrdup (ts ? ts : ""); @@ -53,7 +62,9 @@ Entnode_Create(user, vn, ts, options, tag, date, ts_conflict) /* * Destruct an Entnode */ -void +static void Entnode_Destroy PROTO ((Entnode *)); + +static void Entnode_Destroy (ent) Entnode *ent; { @@ -79,7 +90,14 @@ write_ent_proc (node, closure) Node *node; void *closure; { - if (fputentent(entfile, (Entnode *) node->data)) + Entnode *entnode; + + entnode = (Entnode *) node->data; + + if (closure != NULL && entnode->type != ENT_FILE) + *(int *) closure = 1; + + if (fputentent(entfile, entnode)) error (1, errno, "cannot write %s", entfilename); return (0); @@ -93,10 +111,26 @@ static void write_entries (list) List *list; { + int sawdir; + + sawdir = 0; + /* open the new one and walk the list writing entries */ entfilename = CVSADM_ENTBAK; entfile = open_file (entfilename, "w+"); - (void) walklist (list, write_ent_proc, NULL); + (void) walklist (list, write_ent_proc, (void *) &sawdir); + if (! sawdir) + { + struct stickydirtag *sdtp; + + /* We didn't write out any directories. Check the list + private data to see whether subdirectory information is + known. If it is, we need to write out an empty D line. */ + sdtp = (struct stickydirtag *) list->list->data; + if (sdtp == NULL || sdtp->subdirs) + if (fprintf (entfile, "D\n") < 0) + error (1, errno, "cannot write %s", entfilename); + } if (fclose (entfile) == EOF) error (1, errno, "error closing %s", entfilename); @@ -128,13 +162,26 @@ Scratch_Entry (list, fname) /* hashlookup to see if it is there */ if ((node = findnode_fn (list, fname)) != NULL) { + if (!noexec) + { + entfilename = CVSADM_ENTLOG; + entfile = open_file (entfilename, "a"); + + if (fprintf (entfile, "R ") < 0) + error (1, errno, "cannot write %s", entfilename); + + write_ent_proc (node, NULL); + + if (fclose (entfile) == EOF) + error (1, errno, "error closing %s", entfilename); + } + delnode (node); /* delete the node */ + #ifdef SERVER_SUPPORT if (server_active) server_scratch (fname); #endif - if (!noexec) - write_entries (list); /* re-write the file */ } } @@ -179,17 +226,22 @@ Register (list, fname, vn, ts, options, tag, date, ts_conflict) #endif } - entnode = Entnode_Create(fname, vn, ts, options, tag, date, ts_conflict); + entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date, + ts_conflict); node = AddEntryNode (list, entnode); if (!noexec) { - entfile = open_file (CVSADM_ENTLOG, "a"); - + entfilename = CVSADM_ENTLOG; + entfile = open_file (entfilename, "a"); + + if (fprintf (entfile, "A ") < 0) + error (1, errno, "cannot write %s", entfilename); + write_ent_proc (node, NULL); if (fclose (entfile) == EOF) - error (1, errno, "error closing %s", CVSADM_ENTLOG); + error (1, errno, "error closing %s", entfilename); } } @@ -207,20 +259,21 @@ freesdt (p) free (sdtp->tag); if (sdtp->date) free (sdtp->date); - if (sdtp->options) - free (sdtp->options); free ((char *) sdtp); } static Entnode * -fgetentent(fpin) +fgetentent(fpin, cmd, sawdir) FILE *fpin; + char *cmd; + int *sawdir; { Entnode *ent; char *line; size_t line_chars_allocated; register char *cp; - char *user, *vn, *ts, *options; + enum ent_type type; + char *l, *user, *vn, *ts, *options; char *tag_or_date, *tag, *date, *ts_conflict; line = NULL; @@ -229,10 +282,39 @@ fgetentent(fpin) ent = NULL; while (getline (&line, &line_chars_allocated, fpin) > 0) { - if (line[0] != '/') + l = line; + + /* If CMD is not NULL, we are reading an Entries.Log file. + Each line in the Entries.Log file starts with a single + character command followed by a space. For backward + compatibility, the absence of a space indicates an add + command. */ + if (cmd != NULL) + { + if (l[1] != ' ') + *cmd = 'A'; + else + { + *cmd = l[0]; + l += 2; + } + } + + type = ENT_FILE; + + if (l[0] == 'D') + { + type = ENT_SUBDIR; + *sawdir = 1; + ++l; + /* An empty D line is permitted; it is a signal that this + Entries file lists all known subdirectories. */ + } + + if (l[0] != '/') continue; - user = line + 1; + user = l + 1; if ((cp = strchr (user, '/')) == NULL) continue; *cp++ = '\0'; @@ -258,7 +340,7 @@ fgetentent(fpin) tag = tag_or_date + 1; else if (*tag_or_date == 'D') date = tag_or_date + 1; - + if ((ts_conflict = strchr (ts, '+'))) *ts_conflict++ = '\0'; @@ -274,7 +356,7 @@ fgetentent(fpin) */ { struct stat sb; - if (strlen (ts) > 30 && stat (user, &sb) == 0) + if (strlen (ts) > 30 && CVS_STAT (user, &sb) == 0) { char *c = ctime (&sb.st_mtime); @@ -288,7 +370,8 @@ fgetentent(fpin) } } - ent = Entnode_Create(user, vn, ts, options, tag, date, ts_conflict); + ent = Entnode_Create (type, user, vn, ts, options, tag, date, + ts_conflict); break; } @@ -301,6 +384,16 @@ fputentent(fp, p) FILE *fp; Entnode *p; { + switch (p->type) + { + case ENT_FILE: + break; + case ENT_SUBDIR: + if (fprintf (fp, "D") < 0) + return 1; + break; + } + if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0) return 1; if (p->conflict) @@ -339,10 +432,13 @@ Entries_Open (aflag) int aflag; { List *entries; + struct stickydirtag *sdtp = NULL; Entnode *ent; char *dirtag, *dirdate; + int dirnonbranch; int do_rewrite = 0; FILE *fpin; + int sawdir; /* get a fresh list... */ entries = getlist (); @@ -351,28 +447,29 @@ Entries_Open (aflag) * Parse the CVS/Tag file, to get any default tag/date settings. Use * list-private storage to tuck them away for Version_TS(). */ - ParseTag (&dirtag, &dirdate); + ParseTag (&dirtag, &dirdate, &dirnonbranch); if (aflag || dirtag || dirdate) { - struct stickydirtag *sdtp; - sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp)); memset ((char *) sdtp, 0, sizeof (*sdtp)); sdtp->aflag = aflag; sdtp->tag = xstrdup (dirtag); sdtp->date = xstrdup (dirdate); + sdtp->nonbranch = dirnonbranch; /* feed it into the list-private area */ entries->list->data = (char *) sdtp; entries->list->delproc = freesdt; } - fpin = fopen (CVSADM_ENT, "r"); + sawdir = 0; + + fpin = CVS_FOPEN (CVSADM_ENT, "r"); if (fpin == NULL) error (0, errno, "cannot open %s for reading", CVSADM_ENT); else { - while ((ent = fgetentent (fpin)) != NULL) + while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL) { (void) AddEntryNode (entries, ent); } @@ -380,17 +477,48 @@ Entries_Open (aflag) fclose (fpin); } - fpin = fopen (CVSADM_ENTLOG, "r"); + fpin = CVS_FOPEN (CVSADM_ENTLOG, "r"); if (fpin != NULL) { - while ((ent = fgetentent (fpin)) != NULL) + char cmd; + Node *node; + + while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL) { - (void) AddEntryNode (entries, ent); + switch (cmd) + { + case 'A': + (void) AddEntryNode (entries, ent); + break; + case 'R': + node = findnode_fn (entries, ent->user); + if (node != NULL) + delnode (node); + Entnode_Destroy (ent); + break; + default: + /* Ignore unrecognized commands. */ + break; + } } do_rewrite = 1; fclose (fpin); } + /* Update the list private data to indicate whether subdirectory + information is known. Nonexistent list private data is taken + to mean that it is known. */ + if (sdtp != NULL) + sdtp->subdirs = sawdir; + else if (! sawdir) + { + sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp)); + memset ((char *) sdtp, 0, sizeof (*sdtp)); + sdtp->subdirs = 0; + entries->list->data = (char *) sdtp; + entries->list->delproc = freesdt; + } + if (do_rewrite && !noexec) write_entries (entries); @@ -471,17 +599,23 @@ AddEntryNode (list, entdata) * Write out/Clear the CVS/Tag file. */ void -WriteTag (dir, tag, date) +WriteTag (dir, tag, date, nonbranch, update_dir, repository) char *dir; char *tag; char *date; + int nonbranch; + char *update_dir; + char *repository; { FILE *fout; - char tmp[PATH_MAX]; + char *tmp; if (noexec) return; + tmp = xmalloc ((dir ? strlen (dir) : 0) + + sizeof (CVSADM_TAG) + + 10); if (dir == NULL) (void) strcpy (tmp, CVSADM_TAG); else @@ -492,8 +626,16 @@ WriteTag (dir, tag, date) fout = open_file (tmp, "w+"); if (tag) { - if (fprintf (fout, "T%s\n", tag) < 0) - error (1, errno, "write to %s failed", tmp); + if (nonbranch) + { + if (fprintf (fout, "N%s\n", tag) < 0) + error (1, errno, "write to %s failed", tmp); + } + else + { + if (fprintf (fout, "T%s\n", tag) < 0) + error (1, errno, "write to %s failed", tmp); + } } else { @@ -506,15 +648,32 @@ WriteTag (dir, tag, date) else if (unlink_file (tmp) < 0 && ! existence_error (errno)) error (1, errno, "cannot remove %s", tmp); + free (tmp); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_sticky (update_dir, repository, tag, date, nonbranch); +#endif } -/* - * Parse the CVS/Tag file for the current directory. - */ +/* Parse the CVS/Tag file for the current directory. + + If it contains a date, sets *DATEP to the date in a newly malloc'd + string, *TAGP to NULL, and *NONBRANCHP to an unspecified value. + + If it contains a branch tag, sets *TAGP to the tag in a newly + malloc'd string, *NONBRANCHP to 0, and *DATEP to NULL. + + If it contains a nonbranch tag, sets *TAGP to the tag in a newly + malloc'd string, *NONBRANCHP to 1, and *DATEP to NULL. + + If it does not exist, or contains something unrecognized by this + version of CVS, set *DATEP and *TAGP to NULL and *NONBRANCHP to + an unspecified value. */ void -ParseTag (tagp, datep) +ParseTag (tagp, datep, nonbranchp) char **tagp; char **datep; + int *nonbranchp; { FILE *fp; @@ -522,7 +681,12 @@ ParseTag (tagp, datep) *tagp = (char *) NULL; if (datep) *datep = (char *) NULL; - fp = fopen (CVSADM_TAG, "r"); + /* Always store a value here, even in the 'D' case where the value + is unspecified. Shuts up tools which check for references to + uninitialized memory. */ + if (nonbranchp != NULL) + *nonbranchp = 0; + fp = CVS_FOPEN (CVSADM_TAG, "r"); if (fp) { char *line; @@ -531,18 +695,195 @@ ParseTag (tagp, datep) line = NULL; line_chars_allocated = 0; - + if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0) { /* Remove any trailing newline. */ if (line[line_length - 1] == '\n') line[--line_length] = '\0'; - if (*line == 'T' && tagp) - *tagp = xstrdup (line + 1); - else if (*line == 'D' && datep) - *datep = xstrdup (line + 1); + switch (*line) + { + case 'T': + if (tagp != NULL) + *tagp = xstrdup (line + 1); + break; + case 'D': + if (datep != NULL) + *datep = xstrdup (line + 1); + break; + case 'N': + if (tagp != NULL) + *tagp = xstrdup (line + 1); + if (nonbranchp != NULL) + *nonbranchp = 1; + break; + default: + /* Silently ignore it; it may have been + written by a future version of CVS which extends the + syntax. */ + break; + } } (void) fclose (fp); free (line); } } + +/* + * This is called if all subdirectory information is known, but there + * aren't any subdirectories. It records that fact in the list + * private data. + */ + +void +Subdirs_Known (entries) + List *entries; +{ + struct stickydirtag *sdtp; + + /* If there is no list private data, that means that the + subdirectory information is known. */ + sdtp = (struct stickydirtag *) entries->list->data; + if (sdtp != NULL && ! sdtp->subdirs) + { + FILE *fp; + + sdtp->subdirs = 1; + if (!noexec) + { + /* Create Entries.Log so that Entries_Close will do something. */ + fp = open_file (CVSADM_ENTLOG, "a"); + if (fclose (fp) == EOF) + error (1, errno, "cannot close %s", CVSADM_ENTLOG); + } + } +} + +/* Record subdirectory information. */ + +static Entnode * +subdir_record (cmd, parent, dir) + int cmd; + const char *parent; + const char *dir; +{ + Entnode *entnode; + + /* None of the information associated with a directory is + currently meaningful. */ + entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "", + (char *) NULL, (char *) NULL, + (char *) NULL); + + if (!noexec) + { + if (parent == NULL) + entfilename = CVSADM_ENTLOG; + else + { + entfilename = xmalloc (strlen (parent) + + sizeof CVSADM_ENTLOG + + 10); + sprintf (entfilename, "%s/%s", parent, CVSADM_ENTLOG); + } + + entfile = CVS_FOPEN (entfilename, "a"); + if (entfile == NULL) + { + int save_errno = errno; + + /* It is not an error if there is no CVS administration + directory. Permitting this case simplifies some + calling code. */ + + if (parent == NULL) + { + if (! isdir (CVSADM)) + return entnode; + } + else + { + sprintf (entfilename, "%s/%s", parent, CVSADM); + if (! isdir (entfilename)) + { + free (entfilename); + entfilename = NULL; + return entnode; + } + } + + error (1, save_errno, "cannot open %s", entfilename); + } + + if (fprintf (entfile, "%c ", cmd) < 0) + error (1, errno, "cannot write %s", entfilename); + + if (fputentent (entfile, entnode) != 0) + error (1, errno, "cannot write %s", entfilename); + + if (fclose (entfile) == EOF) + error (1, errno, "error closing %s", entfilename); + + if (parent != NULL) + { + free (entfilename); + entfilename = NULL; + } + } + + return entnode; +} + +/* + * Record the addition of a new subdirectory DIR in PARENT. PARENT + * may be NULL, which means the current directory. ENTRIES is the + * current entries list; it may be NULL, which means that it need not + * be updated. + */ + +void +Subdir_Register (entries, parent, dir) + List *entries; + const char *parent; + const char *dir; +{ + Entnode *entnode; + + /* Ignore attempts to register ".". These can happen in the + server code. */ + if (dir[0] == '.' && dir[1] == '\0') + return; + + entnode = subdir_record ('A', parent, dir); + + if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0)) + (void) AddEntryNode (entries, entnode); + else + Entnode_Destroy (entnode); +} + +/* + * Record the removal of a subdirectory. The arguments are the same + * as for Subdir_Register. + */ + +void +Subdir_Deregister (entries, parent, dir) + List *entries; + const char *parent; + const char *dir; +{ + Entnode *entnode; + + entnode = subdir_record ('R', parent, dir); + Entnode_Destroy (entnode); + + if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0)) + { + Node *p; + + p = findnode_fn (entries, dir); + if (p != NULL) + delnode (p); + } +} diff --git a/contrib/cvs/src/error.c b/contrib/cvs/src/error.c index 8a10cc7..5c7eef5 100644 --- a/contrib/cvs/src/error.c +++ b/contrib/cvs/src/error.c @@ -9,11 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ /* David MacKenzie */ /* Brian Berliner added support for CVS */ @@ -30,7 +26,7 @@ int error_use_protocol; #ifdef HAVE_VPRINTF -#if __STDC__ +#ifdef __STDC__ #include <stdarg.h> #define VA_START(args, lastarg) va_start(args, lastarg) #else /* ! __STDC__ */ @@ -54,38 +50,43 @@ int error_use_protocol; #include <stdlib.h> #include <string.h> #else /* ! STDC_HEADERS */ -#if __STDC__ +#ifdef __STDC__ void exit(int status); #else /* ! __STDC__ */ void exit (); #endif /* __STDC__ */ #endif /* STDC_HEADERS */ +#ifndef strerror extern char *strerror (); +#endif extern int vasprintf (); -typedef void (*fn_returning_void) PROTO((void)); - -/* Function to call before exiting. */ -static fn_returning_void cleanup_fn; - -fn_returning_void -error_set_cleanup (arg) - fn_returning_void arg; +void +error_exit PROTO ((void)) { - fn_returning_void retval = cleanup_fn; - cleanup_fn = arg; - return retval; + Lock_Cleanup(); +#ifdef SERVER_SUPPORT + if (server_active) + server_cleanup (0); +#endif +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } /* Print the program name and error message MESSAGE, which is a printf-style format string with optional args. If ERRNUM is nonzero, print its corresponding system error message. - Exit with status EXIT_FAILURE if STATUS is nonzero. */ + Exit with status EXIT_FAILURE if STATUS is nonzero. If MESSAGE is "", + no need to print a message. */ /* VARARGS */ void -#if defined (HAVE_VPRINTF) && __STDC__ +#if defined (HAVE_VPRINTF) && defined (__STDC__) error (int status, int errnum, const char *message, ...) #else error (status, errnum, message, va_alist) @@ -95,19 +96,10 @@ error (status, errnum, message, va_alist) va_dcl #endif { - FILE *out = stderr; -#ifdef HAVE_VPRINTF - va_list args; -#endif - - if (error_use_protocol) - { - out = stdout; - printf ("E "); - } - #ifdef HAVE_VPRINTF + if (message[0] != '\0') { + va_list args; char *mess = NULL; char *entire; size_t len; @@ -157,10 +149,7 @@ error (status, errnum, message, va_alist) free (mess); } } - if (error_use_protocol) - fputs (entire ? entire : "out of memory", out); - else - cvs_outerr (entire ? entire : "out of memory", 0); + cvs_outerr (entire ? entire : "out of memory\n", 0); if (entire != NULL) free (entire); } @@ -169,45 +158,53 @@ error (status, errnum, message, va_alist) /* I think that all relevant systems have vprintf these days. But just in case, I'm leaving this code here. */ - if (command_name && *command_name) + if (message[0] != '\0') { - if (status) - fprintf (out, "%s [%s aborted]: ", program_name, command_name); + FILE *out = stderr; + + if (error_use_protocol) + { + out = stdout; + printf ("E "); + } + + if (command_name && *command_name) + { + if (status) + fprintf (out, "%s [%s aborted]: ", program_name, command_name); + else + fprintf (out, "%s %s: ", program_name, command_name); + } else - fprintf (out, "%s %s: ", program_name, command_name); - } - else - fprintf (out, "%s: ", program_name); + fprintf (out, "%s: ", program_name); #ifdef HAVE_VPRINTF - VA_START (args, message); - vfprintf (out, message, args); - va_end (args); + VA_START (args, message); + vfprintf (out, message, args); + va_end (args); #else #ifdef HAVE_DOPRNT - _doprnt (message, &args, out); + _doprnt (message, &args, out); #else - fprintf (out, message, a1, a2, a3, a4, a5, a6, a7, a8); + fprintf (out, message, a1, a2, a3, a4, a5, a6, a7, a8); #endif #endif - if (errnum) - fprintf (out, ": %s", strerror (errnum)); - putc ('\n', out); + if (errnum) + fprintf (out, ": %s", strerror (errnum)); + putc ('\n', out); + + /* In the error_use_protocol case, this probably does + something useful. In most other cases, I suspect it is a + noop (either stderr is line buffered or we haven't written + anything to stderr) or unnecessary (if stderr is not line + buffered, maybe there is a reason....). */ + fflush (out); + } #endif /* No HAVE_VPRINTF */ - /* In the error_use_protocol case, this probably does something useful. - In most other cases, I suspect it is a noop (either stderr is line - buffered or we haven't written anything to stderr) or unnecessary - (if stderr is not line buffered, maybe there is a reason....). */ - fflush (out); - if (status) - { - if (cleanup_fn) - (*cleanup_fn) (); - exit (EXIT_FAILURE); - } + error_exit (); } /* Print the program name and error message MESSAGE, which is a printf-style @@ -216,7 +213,7 @@ error (status, errnum, message, va_alist) Exit with status EXIT_FAILURE if STATUS is nonzero. */ /* VARARGS */ void -#if defined (HAVE_VPRINTF) && __STDC__ +#if defined (HAVE_VPRINTF) && defined (__STDC__) fperror (FILE *fp, int status, int errnum, char *message, ...) #else fperror (fp, status, errnum, message, va_alist) @@ -248,9 +245,5 @@ fperror (fp, status, errnum, message, va_alist) putc ('\n', fp); fflush (fp); if (status) - { - if (cleanup_fn) - (*cleanup_fn) (); - exit (EXIT_FAILURE); - } + error_exit (); } diff --git a/contrib/cvs/src/error.h b/contrib/cvs/src/error.h index 7d4f535..e0fde4a 100644 --- a/contrib/cvs/src/error.h +++ b/contrib/cvs/src/error.h @@ -9,15 +9,21 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifndef _error_h_ #define _error_h_ +/* Add prototype support. Normally this is done in cvs.h, but that + doesn't get included from lib/savecwd.c. */ +#ifndef PROTO +#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) +#define PROTO(ARGS) ARGS +#else +#define PROTO(ARGS) () +#endif +#endif + #ifndef __attribute__ /* This feature is available in gcc versions 2.5 and later. */ # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ @@ -31,13 +37,17 @@ # endif #endif -#if __STDC__ +#ifdef __STDC__ void error (int, int, const char *, ...) \ __attribute__ ((__format__ (__printf__, 3, 4))); #else void error (); #endif +/* Exit due to an error. Similar to error (1, 0, "message"), but call + it in the case where the message has already been printed. */ +extern void error_exit PROTO ((void)); + /* If non-zero, error will use the CVS protocol to report error messages. This will only be set in the CVS server parent process; most other code is run via do_cvs_command, which forks off a child diff --git a/contrib/cvs/src/expand_path.c b/contrib/cvs/src/expand_path.c index 9898051..7d0bc39 100644 --- a/contrib/cvs/src/expand_path.c +++ b/contrib/cvs/src/expand_path.c @@ -81,10 +81,12 @@ variable_set (nameval) } /* This routine will expand the pathname to account for ~ and $ - characters as described above. If an error occurs, an error - message is printed via error() and NULL is returned. FILE and - LINE are the filename and linenumber to include in the error - message. */ + characters as described above. Returns a pointer to a newly + malloc'd string. If an error occurs, an error message is printed + via error() and NULL is returned. FILE and LINE are the filename + and linenumber to include in the error message. FILE must point + to something; LINE can be zero to indicate the line number is not + known. */ char * expand_path (name, file, line) char *name; @@ -93,40 +95,82 @@ expand_path (name, file, line) { char *s; char *d; - /* FIXME: arbitrary limit. */ - char mybuf[PATH_MAX]; - char buf[PATH_MAX]; + + char *mybuf = NULL; + size_t mybuf_size = 0; + char *buf = NULL; + size_t buf_size = 0; + + size_t doff; + char *result; + + /* Sorry this routine is so ugly; it is a head-on collision + between the `traditional' unix *d++ style and the need to + dynamically allocate. It would be much cleaner (and probably + faster, not that this is a bottleneck for CVS) with more use of + strcpy & friends, but I haven't taken the effort to rewrite it + thusly. */ + + /* First copy from NAME to MYBUF, expanding $<foo> as we go. */ s = name; d = mybuf; + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; while ((*d++ = *s)) + { if (*s++ == '$') { char *p = d; char *e; int flag = (*s == '{'); + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; for (; (*d++ = *s); s++) + { if (flag ? *s =='}' : isalnum (*s) == 0 && *s != '_') break; - *--d = 0; + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; + } + *--d = '\0'; e = expand_variable (&p[flag], file, line); if (e) { + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; for (d = &p[-1]; (*d++ = *e++);) - ; + { + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; + } --d; if (flag && *s) s++; } else /* expand_variable has already printed an error message. */ - return NULL; + goto error_exit; } - *d = 0; + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; + } + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; + *d = '\0'; + + /* Then copy from MYBUF to BUF, expanding ~. */ s = mybuf; d = buf; /* If you don't want ~username ~/ to be expanded simply remove @@ -156,8 +200,15 @@ expand_path (name, file, line) } t = ps->pw_dir; } + doff = d - buf; + expand_string (&buf, &buf_size, doff + 1); + d = buf + doff; while ((*d++ = *t++)) - ; + { + doff = d - buf; + expand_string (&buf, &buf_size, doff + 1); + d = buf + doff; + } --d; if (*p == 0) *p = '/'; /* always add / */ @@ -166,12 +217,35 @@ expand_path (name, file, line) else --s; /* Kill up to here */ + doff = d - buf; + expand_string (&buf, &buf_size, doff + 1); + d = buf + doff; while ((*d++ = *s++)) - ; - *d=0; - result = xmalloc (sizeof(char) * strlen(buf)+1); - strcpy (result, buf); + { + doff = d - buf; + expand_string (&buf, &buf_size, doff + 1); + d = buf + doff; + } + doff = d - buf; + expand_string (&buf, &buf_size, doff + 1); + d = buf + doff; + *d = '\0'; + + /* OK, buf contains the value we want to return. Clean up and return + it. */ + free (mybuf); + /* Save a little memory with xstrdup; buf will tend to allocate + more than it needs to. */ + result = xstrdup (buf); + free (buf); return result; + + error_exit: + if (mybuf != NULL) + free (mybuf); + if (buf != NULL) + free (buf); + return NULL; } static char * @@ -181,7 +255,7 @@ expand_variable (name, file, line) int line; { if (strcmp (name, CVSROOT_ENV) == 0) - return CVSroot; + return CVSroot_original; else if (strcmp (name, RCSBIN_ENV) == 0) return Rcsbin; else if (strcmp (name, EDITOR1_ENV) == 0) @@ -231,10 +305,10 @@ expand_variable (name, file, line) that various crazy syntaxes might be invented for inserting information about revisions, branches, etc. */ if (line != 0) - error (0, 0, "%s:%d: unrecognized varaible syntax %s", + error (0, 0, "%s:%d: unrecognized variable syntax %s", file, line, name); else - error (0, 0, "%s: unrecognized varaible syntax %s", + error (0, 0, "%s: unrecognized variable syntax %s", file, name); return NULL; } diff --git a/contrib/cvs/src/fileattr.c b/contrib/cvs/src/fileattr.c index 827c69c..eda7753 100644 --- a/contrib/cvs/src/fileattr.c +++ b/contrib/cvs/src/fileattr.c @@ -8,11 +8,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #include "cvs.h" #include "getline.h" @@ -35,6 +31,14 @@ static int attr_read_attempted; /* Have the in-memory attributes been modified since we read them? */ static int attrs_modified; +/* More in-memory attributes: linked list of unrecognized + fileattr lines. We pass these on unchanged. */ +struct unrecog { + char *line; + struct unrecog *next; +}; +static struct unrecog *unrecog_head; + /* Note that if noone calls fileattr_get, this is very cheap. No stat(), no open(), no nothing. */ void @@ -45,6 +49,7 @@ fileattr_startdir (repos) fileattr_stored_repos = xstrdup (repos); assert (attrlist == NULL); attr_read_attempted = 0; + assert (unrecog_head == NULL); } static void @@ -84,7 +89,7 @@ fileattr_read () strcat (fname, CVSREP_FILEATTR); attr_read_attempted = 1; - fp = fopen (fname, FOPEN_BINARY_READ); + fp = CVS_FOPEN (fname, FOPEN_BINARY_READ); if (fp == NULL) { if (!existence_error (errno)) @@ -112,7 +117,12 @@ fileattr_read () newnode->delproc = fileattr_delproc; newnode->key = xstrdup (line + 1); newnode->data = xstrdup (p); - addnode (attrlist, newnode); + if (addnode (attrlist, newnode) != 0) + /* If the same filename appears twice in the file, discard + any line other than the first for that filename. This + is the way that CVS has behaved since file attributes + were first introduced. */ + free (newnode); } else if (line[0] == 'D') { @@ -123,7 +133,17 @@ fileattr_read () ++p; fileattr_default_attrs = xstrdup (p); } - /* else just ignore the line, for future expansion. */ + else + { + /* Unrecognized type, we want to just preserve the line without + changing it, for future expansion. */ + struct unrecog *new; + + new = (struct unrecog *) xmalloc (sizeof (struct unrecog)); + new->line = xstrdup (line); + new->next = unrecog_head; + unrecog_head = new; + } } if (ferror (fp)) error (0, errno, "cannot read %s", fname); @@ -137,8 +157,8 @@ fileattr_read () char * fileattr_get (filename, attrname) - char *filename; - char *attrname; + const char *filename; + const char *attrname; { Node *node; size_t attrname_len = strlen (attrname); @@ -180,8 +200,8 @@ fileattr_get (filename, attrname) char * fileattr_get0 (filename, attrname) - char *filename; - char *attrname; + const char *filename; + const char *attrname; { char *cp; char *cpend; @@ -202,8 +222,8 @@ fileattr_get0 (filename, attrname) char * fileattr_modify (list, attrname, attrval, namevalsep, entsep) char *list; - char *attrname; - char *attrval; + const char *attrname; + const char *attrval; int namevalsep; int entsep; { @@ -298,15 +318,13 @@ fileattr_modify (list, attrname, attrval, namevalsep, entsep) void fileattr_set (filename, attrname, attrval) - char *filename; - char *attrname; - char *attrval; + const char *filename; + const char *attrname; + const char *attrval; { Node *node; char *p; - attrs_modified = 1; - if (filename == NULL) { p = fileattr_modify (fileattr_default_attrs, attrname, attrval, @@ -314,6 +332,7 @@ fileattr_set (filename, attrname, attrval) if (fileattr_default_attrs != NULL) free (fileattr_default_attrs); fileattr_default_attrs = p; + attrs_modified = 1; return; } if (attrlist == NULL) @@ -346,17 +365,20 @@ fileattr_set (filename, attrname, attrval) } p = fileattr_modify (node->data, attrname, attrval, '=', ';'); - free (node->data); - node->data = NULL; if (p == NULL) delnode (node); else + { + free (node->data); node->data = p; + } + + attrs_modified = 1; } void fileattr_newfile (filename) - char *filename; + const char *filename; { Node *node; @@ -423,7 +445,9 @@ fileattr_write () strcat (fname, "/"); strcat (fname, CVSREP_FILEATTR); - if (list_isempty (attrlist) && fileattr_default_attrs == NULL) + if (list_isempty (attrlist) + && fileattr_default_attrs == NULL + && unrecog_head == NULL) { /* There are no attributes. */ if (unlink_file (fname) < 0) @@ -440,7 +464,7 @@ fileattr_write () strcpy (fname, fileattr_stored_repos); strcat (fname, "/"); strcat (fname, CVSREP); - if (rmdir (fname) < 0) + if (CVS_RMDIR (fname) < 0) { if (errno != ENOTEMPTY @@ -456,7 +480,7 @@ fileattr_write () } omask = umask (cvsumask); - fp = fopen (fname, FOPEN_BINARY_WRITE); + fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE); if (fp == NULL) { if (existence_error (errno)) @@ -481,7 +505,7 @@ fileattr_write () } free (repname); - fp = fopen (fname, FOPEN_BINARY_WRITE); + fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE); } if (fp == NULL) { @@ -491,13 +515,32 @@ fileattr_write () } } (void) umask (omask); + + /* First write the "F" attributes. */ walklist (attrlist, writeattr_proc, fp); + + /* Then the "D" attribute. */ if (fileattr_default_attrs != NULL) { fputs ("D\t", fp); fputs (fileattr_default_attrs, fp); fputs ("\012", fp); } + + /* Then any other attributes. */ + while (unrecog_head != NULL) + { + struct unrecog *p; + + p = unrecog_head; + fputs (p->line, fp); + fputs ("\012", fp); + + unrecog_head = p->next; + free (p->line); + free (p); + } + if (fclose (fp) < 0) error (0, errno, "cannot close %s", fname); attrs_modified = 0; diff --git a/contrib/cvs/src/fileattr.h b/contrib/cvs/src/fileattr.h index c24c035..c6362af 100644 --- a/contrib/cvs/src/fileattr.h +++ b/contrib/cvs/src/fileattr.h @@ -8,11 +8,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #ifndef FILEATTR_H @@ -27,6 +23,13 @@ ENT-TYPE is 'D', and FILENAME empty, to specify default attributes to be used for newly added files. + Other ENT-TYPE are reserved for future expansion. CVS 1.9 and older + will delete them any time it writes file attributes. Current versions + of CVS will preserve them. + + Note that the order of the line is not significant; CVS is free to + rearrange them at its convenience. + There is currently no way of quoting tabs or linefeeds in the filename, '=' in ATTRNAME, ';' in ATTRVAL, etc. I'm not sure whether I think we need one. Note: the current implementation also @@ -66,11 +69,12 @@ extern void fileattr_startdir PROTO ((char *repos)); by '\0' or ';'. Return NULL if said file lacks said attribute. If FILENAME is NULL, return default attributes (attributes for files created in the future). */ -extern char *fileattr_get PROTO ((char *filename, char *attrname)); +extern char *fileattr_get PROTO ((const char *filename, const char *attrname)); /* Like fileattr_get, but return a pointer to a newly malloc'd string terminated by '\0' (or NULL if said file lacks said attribute). */ -extern char *fileattr_get0 PROTO ((char *filename, char *attrname)); +extern char *fileattr_get0 PROTO ((const char *filename, + const char *attrname)); /* This is just a string manipulation function; it does not manipulate file attributes as such. @@ -94,26 +98,26 @@ extern char *fileattr_get0 PROTO ((char *filename, char *attrname)); => "abc=val;def=v2" fileattr_modify ("abc=v1;def=v2", "def", "val", '=', ';')) => "abc=v1;def=val" - fileattr_modify ("abc=v1;def=v2", "xxx", "val")) + fileattr_modify ("abc=v1;def=v2", "xxx", "val", '=', ';')) => "abc=v1;def=v2;xxx=val" fileattr_modify ("abc=v1;def=v2;ghi=v3", "def", "val", '=', ';')) => "abc=v1;def=val;ghi=v3" */ -extern char *fileattr_modify PROTO ((char *list, char *attrname, - char *attrval, int namevalsep, +extern char *fileattr_modify PROTO ((char *list, const char *attrname, + const char *attrval, int namevalsep, int entsep)); /* Set attribute ATTRNAME for file FILENAME to ATTRVAL. If ATTRVAL is NULL, the attribute is removed. Changes are not written to disk until the next call to fileattr_write. If FILENAME is NULL, set attributes for files created in the future. If ATTRVAL is NULL, remove that attribute. */ -extern void fileattr_set PROTO ((char *filename, char *attrname, - char *attrval)); +extern void fileattr_set PROTO ((const char *filename, const char *attrname, + const char *attrval)); /* Set the attributes for file FILENAME in whatever manner is appropriate for a newly created file. */ -extern void fileattr_newfile PROTO ((char *filename)); +extern void fileattr_newfile PROTO ((const char *filename)); /* Write out all modified attributes. */ extern void fileattr_write PROTO ((void)); diff --git a/contrib/cvs/src/filesubr.c b/contrib/cvs/src/filesubr.c index 086da83..71df566 100644 --- a/contrib/cvs/src/filesubr.c +++ b/contrib/cvs/src/filesubr.c @@ -11,11 +11,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ /* These functions were moved out of subr.c because they need different definitions under operating systems (like, say, Windows NT) with different @@ -23,14 +19,6 @@ #include "cvs.h" -/* - * I don't know of a convenient way to test this at configure time, or else - * I'd certainly do it there. - */ -#if defined(NeXT) -#define LOSING_TMPNAM_FUNCTION -#endif - static int deep_remove_dir PROTO((const char *path)); /* @@ -290,6 +278,22 @@ make_directories (name) (void) mkdir (name, 0777); } +/* Create directory NAME if it does not already exist; fatal error for + other errors. Returns 0 if directory was created; 1 if it already + existed. */ +int +mkdir_if_needed (name) + char *name; +{ + if (mkdir (name, 0777) < 0) + { + if (errno != EEXIST) + error (1, errno, "cannot make directory %s", name); + return 1; + } + return 0; +} + /* * Change the mode of a file, either adding write permissions, or removing * all write permissions. Either change honors the current umask setting. @@ -451,47 +455,62 @@ deep_remove_dir (path) { DIR *dirp; struct dirent *dp; - char buf[PATH_MAX]; - if (rmdir (path) != 0 && (errno == ENOTEMPTY || errno == EEXIST)) + if (rmdir (path) != 0) { - if ((dirp = opendir (path)) == NULL) - /* If unable to open the directory return - * an error - */ - return -1; - - while ((dp = readdir (dirp)) != NULL) + if (errno == ENOTEMPTY + || errno == EEXIST + /* Ugly workaround for ugly AIX 4.1 (and 3.2) header bug + (it defines ENOTEMPTY and EEXIST to 17 but actually + returns 87). */ + || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87)) { - if (strcmp (dp->d_name, ".") == 0 || - strcmp (dp->d_name, "..") == 0) - continue; + if ((dirp = opendir (path)) == NULL) + /* If unable to open the directory return + * an error + */ + return -1; - sprintf (buf, "%s/%s", path, dp->d_name); - - /* See comment in unlink_file_dir explanation of why we use - isdir instead of just calling unlink and checking the - status. */ - if (isdir(buf)) + while ((dp = readdir (dirp)) != NULL) { - if (deep_remove_dir(buf)) + char *buf; + + if (strcmp (dp->d_name, ".") == 0 || + strcmp (dp->d_name, "..") == 0) + continue; + + buf = xmalloc (strlen (path) + strlen (dp->d_name) + 5); + sprintf (buf, "%s/%s", path, dp->d_name); + + /* See comment in unlink_file_dir explanation of why we use + isdir instead of just calling unlink and checking the + status. */ + if (isdir(buf)) { - closedir(dirp); - return -1; + if (deep_remove_dir(buf)) + { + closedir(dirp); + free (buf); + return -1; + } } - } - else - { - if (unlink (buf) != 0) + else { - closedir(dirp); - return -1; + if (unlink (buf) != 0) + { + closedir(dirp); + free (buf); + return -1; + } } + free (buf); } + closedir (dirp); + return rmdir (path); } - closedir (dirp); - return rmdir (path); - } + else + return -1; + } /* Was able to remove the directory return 0 */ return 0; @@ -596,24 +615,55 @@ xcmp (file1, file2) (void) close (fd2); return (ret); } - -#ifdef LOSING_TMPNAM_FUNCTION -char *tmpnam(char *s) + +/* Generate a unique temporary filename. Returns a pointer to a newly + malloc'd string containing the name. Returns successfully or not at + all. */ +/* There are at least three functions for generating temporary + filenames. We use tempnam (SVID 3) if possible, else mktemp (BSD + 4.3), and as last resort tmpnam (POSIX). Reason is that tempnam and + mktemp both allow to specify the directory in which the temporary + file will be created. */ +#ifdef HAVE_TEMPNAM +char * +cvs_temp_name () { - static char value[L_tmpnam+1]; - - if (s){ - strcpy(s,"/tmp/cvsXXXXXX"); - mktemp(s); - return s; - }else{ - strcpy(value,"/tmp/cvsXXXXXX"); - mktemp(s); - return value; - } + char *retval; + + retval = tempnam (Tmpdir, "cvs"); + if (retval == NULL) + error (1, errno, "cannot generate temporary filename"); + /* tempnam returns a pointer to a newly malloc'd string, so there's + no need for a xstrdup */ + return retval; +} +#else +char * +cvs_temp_name () +{ +# ifdef HAVE_MKTEMP + char *value; + char *retval; + + value = xmalloc (strlen (Tmpdir) + 40); + sprintf (value, "%s/%s", Tmpdir, "cvsXXXXXX" ); + retval = mktemp (value); + + if (retval == NULL) + error (1, errno, "cannot generate temporary filename"); + return value; +# else + char value[L_tmpnam + 1]; + char *retval; + + retval = tmpnam (value); + if (retval == NULL) + error (1, errno, "cannot generate temporary filename"); + return xstrdup (value); +# endif } #endif - + /* Return non-zero iff FILENAME is absolute. Trivial under Unix, but more complicated under other systems. */ int @@ -638,11 +688,28 @@ last_component (path) } /* Return the home directory. Returns a pointer to storage - managed by this function or its callees (currently getenv). */ + managed by this function or its callees (currently getenv). + This function will return the same thing every time it is + called. */ char * get_homedir () { - return getenv ("HOME"); + static char *home = NULL; + char *env = getenv ("HOME"); + struct passwd *pw; + + if (home != NULL) + return home; + + if (env) + home = env; + else if ((pw = (struct passwd *) getpwuid (getuid ())) + && pw->pw_dir) + home = xstrdup (pw->pw_dir); + else + return 0; + + return home; } /* See cvs.h for description. On unix this does nothing, because the @@ -660,3 +727,132 @@ expand_wild (argc, argv, pargc, pargv) for (i = 0; i < argc; ++i) (*pargv)[i] = xstrdup (argv[i]); } + +#ifdef SERVER_SUPPORT +/* Case-insensitive string compare. I know that some systems + have such a routine, but I'm not sure I see any reasons for + dealing with the hair of figuring out whether they do (I haven't + looked into whether this is a performance bottleneck; I would guess + not). */ +int +cvs_casecmp (str1, str2) + char *str1; + char *str2; +{ + char *p; + char *q; + int pqdiff; + + p = str1; + q = str2; + while ((pqdiff = tolower (*p) - tolower (*q)) == 0) + { + if (*p == '\0') + return 0; + ++p; + ++q; + } + return pqdiff; +} + +/* Case-insensitive file open. As you can see, this is an expensive + call. We don't regard it as our main strategy for dealing with + case-insensitivity. Returns errno code or 0 for success. Puts the + new file in *FP. NAME and MODE are as for fopen. If PATHP is not + NULL, then put a malloc'd string containing the pathname as found + into *PATHP. *PATHP is only set if the return value is 0. + + Might be cleaner to separate the file finding (which just gives + *PATHP) from the file opening (which the caller can do). For one + thing, might make it easier to know whether to put NAME or *PATHP + into error messages. */ +int +fopen_case (name, mode, fp, pathp) + char *name; + char *mode; + FILE **fp; + char **pathp; +{ + struct dirent *dp; + DIR *dirp; + char *dir; + char *fname; + char *found_name; + int retval; + + /* Separate NAME into directory DIR and filename within the directory + FNAME. */ + dir = xstrdup (name); + fname = strrchr (dir, '/'); + if (fname == NULL) + error (1, 0, "internal error: relative pathname in fopen_case"); + *fname++ = '\0'; + + found_name = NULL; + dirp = CVS_OPENDIR (dir); + if (dirp == NULL) + { + if (existence_error (errno)) + { + /* This can happen if we are looking in the Attic and the Attic + directory does not exist. Return the error to the caller; + they know what to do with it. */ + retval = errno; + goto out; + } + else + { + /* Give a fatal error; that way the error message can be + more specific than if we returned the error to the caller. */ + error (1, errno, "cannot read directory %s", dir); + } + } + errno = 0; + while ((dp = readdir (dirp)) != NULL) + { + if (cvs_casecmp (dp->d_name, fname) == 0) + { + if (found_name != NULL) + error (1, 0, "%s is ambiguous; could mean %s or %s", + fname, dp->d_name, found_name); + found_name = xstrdup (dp->d_name); + } + } + if (errno != 0) + error (1, errno, "cannot read directory %s", dir); + closedir (dirp); + + if (found_name == NULL) + { + *fp = NULL; + retval = ENOENT; + } + else + { + char *p; + + /* Copy the found name back into DIR. We are assuming that + found_name is the same length as fname, which is true as + long as the above code is just ignoring case and not other + aspects of filename syntax. */ + p = dir + strlen (dir); + *p++ = '/'; + strcpy (p, found_name); + *fp = fopen (dir, mode); + if (*fp == NULL) + retval = errno; + else + retval = 0; + } + + if (pathp == NULL) + free (dir); + else if (retval != 0) + free (dir); + else + *pathp = dir; + free (found_name); + out: + return retval; +} +#endif /* SERVER_SUPPORT */ diff --git a/contrib/cvs/src/find_names.c b/contrib/cvs/src/find_names.c index 4885437..a7a91bb 100644 --- a/contrib/cvs/src/find_names.c +++ b/contrib/cvs/src/find_names.c @@ -18,8 +18,11 @@ #include "cvs.h" -static int find_dirs PROTO((char *dir, List * list, int checkadm)); +static int find_dirs PROTO((char *dir, List * list, int checkadm, + List *entries)); static int find_rcs PROTO((char *dir, List * list)); +static int add_subdir_proc PROTO((Node *, void *)); +static int register_subdir_proc PROTO((Node *, void *)); static List *filelist; @@ -32,8 +35,13 @@ add_entries_proc (node, closure) Node *node; void *closure; { + Entnode *entnode; Node *fnode; + entnode = (Entnode *) node->data; + if (entnode->type != ENT_FILE) + return (0); + fnode = getnode (); fnode->type = FILES; fnode->key = xstrdup (node->key); @@ -63,7 +71,6 @@ Find_Names (repository, which, aflag, optentries) { List *entries; List *files; - char dir[PATH_MAX]; /* make a list for the files */ files = filelist = getlist (); @@ -95,8 +102,11 @@ Find_Names (repository, which, aflag, optentries) /* search the attic too */ if (which & W_ATTIC) { + char *dir; + dir = xmalloc (strlen (repository) + sizeof (CVSATTIC) + 10); (void) sprintf (dir, "%s/%s", repository, CVSATTIC); (void) find_rcs (dir, files); + free (dir); } } @@ -106,12 +116,55 @@ Find_Names (repository, which, aflag, optentries) } /* + * Add an entry from the subdirs list to the directories list. This + * is called via walklist. + */ + +static int +add_subdir_proc (p, closure) + Node *p; + void *closure; +{ + List *dirlist = (List *) closure; + Entnode *entnode; + Node *dnode; + + entnode = (Entnode *) p->data; + if (entnode->type != ENT_SUBDIR) + return 0; + + dnode = getnode (); + dnode->type = DIRS; + dnode->key = xstrdup (entnode->user); + if (addnode (dirlist, dnode) != 0) + freenode (dnode); + return 0; +} + +/* + * Register a subdirectory. This is called via walklist. + */ + +/*ARGSUSED*/ +static int +register_subdir_proc (p, closure) + Node *p; + void *closure; +{ + List *entries = (List *) closure; + + Subdir_Register (entries, (char *) NULL, p->key); + return 0; +} + +/* * create a list of directories to traverse from the current directory */ List * -Find_Directories (repository, which) +Find_Directories (repository, which, entries) char *repository; int which; + List *entries; { List *dirlist; @@ -121,28 +174,60 @@ Find_Directories (repository, which) /* find the local ones */ if (which & W_LOCAL) { - /* look only for CVS controlled sub-directories */ - if (find_dirs (".", dirlist, 1) != 0) - error (1, errno, "cannot open current directory"); + List *tmpentries; + struct stickydirtag *sdtp; + + /* Look through the Entries file. */ + + if (entries != NULL) + tmpentries = entries; + else if (isfile (CVSADM_ENT)) + tmpentries = Entries_Open (0); + else + tmpentries = NULL; + + if (tmpentries != NULL) + sdtp = (struct stickydirtag *) tmpentries->list->data; + + /* If we do have an entries list, then if sdtp is NULL, or if + sdtp->subdirs is nonzero, all subdirectory information is + recorded in the entries list. */ + if (tmpentries != NULL && (sdtp == NULL || sdtp->subdirs)) + walklist (tmpentries, add_subdir_proc, (void *) dirlist); + else + { + /* This is an old working directory, in which subdirectory + information is not recorded in the Entries file. Find + the subdirectories the hard way, and, if possible, add + it to the Entries file for next time. */ + if (find_dirs (".", dirlist, 1, tmpentries) != 0) + error (1, errno, "cannot open current directory"); + if (tmpentries != NULL) + { + if (! list_isempty (dirlist)) + walklist (dirlist, register_subdir_proc, + (void *) tmpentries); + else + Subdirs_Known (tmpentries); + } + } + + if (entries == NULL && tmpentries != NULL) + Entries_Close (tmpentries); } /* look for sub-dirs in the repository */ if ((which & W_REPOS) && repository) { /* search the repository */ - if (find_dirs (repository, dirlist, 0) != 0) + if (find_dirs (repository, dirlist, 0, entries) != 0) error (1, errno, "cannot open directory %s", repository); -#ifdef ATTIC_DIR_SUPPORT /* XXX - FIXME */ - /* search the attic too */ - if (which & W_ATTIC) - { - char dir[PATH_MAX]; - - (void) sprintf (dir, "%s/%s", repository, CVSATTIC); - (void) find_dirs (dir, dirlist, 0); - } -#endif + /* We don't need to look in the attic because directories + never go in the attic. In the future, there hopefully will + be a better mechanism for detecting whether a directory in + the repository is alive or dead; it may or may not involve + moving directories to the attic. */ } /* sort the list into alphabetical order and return it */ @@ -165,7 +250,7 @@ find_rcs (dir, list) DIR *dirp; /* set up to read the dir */ - if ((dirp = opendir (dir)) == NULL) + if ((dirp = CVS_OPENDIR (dir)) == NULL) return (1); /* read the dir, grabbing the ,v files */ @@ -189,23 +274,27 @@ find_rcs (dir, list) } /* - * Finds all the subdirectories of the argument dir and adds them to the - * specified list. Sub-directories without a CVS administration directory - * are optionally ignored Returns 0 for success or 1 on error. + * Finds all the subdirectories of the argument dir and adds them to + * the specified list. Sub-directories without a CVS administration + * directory are optionally ignored. If ENTRIES is not NULL, all + * files on the list are ignored. Returns 0 for success or 1 on + * error. */ static int -find_dirs (dir, list, checkadm) +find_dirs (dir, list, checkadm, entries) char *dir; List *list; int checkadm; + List *entries; { Node *p; - char tmp[PATH_MAX]; + char *tmp = NULL; + size_t tmp_size = 0; struct dirent *dp; DIR *dirp; /* set up to read the dir */ - if ((dirp = opendir (dir)) == NULL) + if ((dirp = CVS_OPENDIR (dir)) == NULL) return (1); /* read the dir, grabbing sub-dirs */ @@ -218,6 +307,12 @@ find_dirs (dir, list, checkadm) strcmp (dp->d_name, CVSREP) == 0) continue; + /* findnode() is going to be significantly faster than stat() + because it involves no system calls. That is why we bother + with the entries argument, and why we check this first. */ + if (entries != NULL && findnode (entries, dp->d_name) != NULL) + continue; + #ifdef DT_DIR if (dp->d_type != DT_DIR) { @@ -228,6 +323,9 @@ find_dirs (dir, list, checkadm) if (fnmatch (RCSPAT, dp->d_name, 0) == 0) continue; + expand_string (&tmp, + &tmp_size, + strlen (dir) + strlen (dp->d_name) + 10); sprintf (tmp, "%s/%s", dir, dp->d_name); if (!isdir (tmp)) continue; @@ -247,6 +345,8 @@ find_dirs (dir, list, checkadm) if (dp->d_type == DT_LNK) continue; #endif + /* Note that we only get here if we already set tmp + above. */ if (islink (tmp)) continue; #ifdef DT_DIR @@ -254,6 +354,10 @@ find_dirs (dir, list, checkadm) #endif /* check for new style */ + expand_string (&tmp, + &tmp_size, + (strlen (dir) + strlen (dp->d_name) + + sizeof (CVSADM) + 10)); (void) sprintf (tmp, "%s/%s/%s", dir, dp->d_name, CVSADM); if (!isdir (tmp)) continue; @@ -267,5 +371,7 @@ find_dirs (dir, list, checkadm) freenode (p); } (void) closedir (dirp); + if (tmp != NULL) + free (tmp); return (0); } diff --git a/contrib/cvs/src/hash.c b/contrib/cvs/src/hash.c index 2197db0..a978d1f 100644 --- a/contrib/cvs/src/hash.c +++ b/contrib/cvs/src/hash.c @@ -10,7 +10,10 @@ #include "cvs.h" #include <assert.h> -/* global caches */ +/* Global caches. The idea is that we maintain a linked list of "free"d + nodes or lists, and get new items from there. It has been suggested + to use an obstack instead, but off the top of my head, I'm not sure + that would gain enough to be worth worrying about. */ static List *listcache = NULL; static Node *nodecache = NULL; @@ -98,15 +101,27 @@ dellist (listp) if ((p = (*listp)->hasharray[i]) != (Node *) NULL) { /* put the nodes into the cache */ +#ifndef NOCACHE p->type = UNKNOWN; p->next = nodecache; nodecache = p; +#else + /* If NOCACHE is defined we turn off the cache. This can make + it easier to tools to determine where items were allocated + and freed, for tracking down memory leaks and the like. */ + free (p); +#endif } } /* put it on the cache */ +#ifndef NOCACHE (*listp)->next = listcache; listcache = *listp; +#else + free ((*listp)->list); + free (*listp); +#endif *listp = (List *) NULL; } @@ -195,9 +210,13 @@ freenode (p) freenode_mem (p); /* then put it in the cache */ +#ifndef NOCACHE p->type = UNKNOWN; p->next = nodecache; nodecache = p; +#else + free (p); +#endif } /* @@ -332,6 +351,19 @@ list_isempty (list) return list == NULL || list->list->next == list->list; } +static int (*client_comp) PROTO ((const Node *, const Node *)); +static int qsort_comp PROTO ((const void *, const void *)); + +static int +qsort_comp (elem1, elem2) + const void *elem1; + const void *elem2; +{ + Node **node1 = (Node **) elem1; + Node **node2 = (Node **) elem2; + return client_comp (*node1, *node2); +} + /* * sort the elements of a list (in place) */ @@ -340,49 +372,48 @@ sortlist (list, comp) List *list; int (*comp) PROTO ((const Node *, const Node *)); { - Node *head, *remain, *p, *q; + Node *head, *remain, *p, **array; + int i, n; /* save the old first element of the list */ head = list->list; remain = head->next; - /* make the header node into a null list of it's own */ - head->next = head->prev = head; + /* count the number of nodes in the list */ + n = 0; + for (p = remain; p != head; p = p->next) + n++; - /* while there are nodes remaining, do insert sort */ - while (remain != head) - { - /* take one from the list */ - p = remain; - remain = remain->next; + /* allocate an array of nodes and populate it */ + array = (Node **) xmalloc (sizeof(Node *) * n); + i = 0; + for (p = remain; p != head; p = p->next) + array[i++] = p; - /* traverse the sorted list looking for the place to insert it */ - for (q = head->next; q != head; q = q->next) - { - if (comp (p, q) < 0) - { - /* p comes before q */ - p->next = q; - p->prev = q->prev; - p->prev->next = p; - q->prev = p; - break; - } - } - if (q == head) - { - /* it belongs at the end of the list */ - p->next = head; - p->prev = head->prev; - p->prev->next = p; - head->prev = p; - } + /* sort the array of nodes */ + client_comp = comp; + qsort (array, n, sizeof(Node *), qsort_comp); + + /* rebuild the list from beginning to end */ + head->next = head->prev = head; + for (i = 0; i < n; i++) + { + p = array[i]; + p->next = head; + p->prev = head->prev; + p->prev->next = p; + head->prev = p; } + + /* release the array of nodes */ + free (array); } /* Debugging functions. Quite useful to call from within gdb. */ -char * +static char *nodetypestring PROTO ((Ntype)); + +static char * nodetypestring (type) Ntype type; { @@ -400,6 +431,7 @@ nodetypestring (type) case NDBMNODE: return("NDBMNODE"); case FILEATTR: return("FILEATTR"); case VARIABLE: return("VARIABLE"); + case RCSFIELD: return("RCSFIELD"); } return("<trash>"); @@ -423,6 +455,11 @@ printnode (node, closure) return(0); } +/* This is global, not static, so that its name is unique and to avoid + compiler warnings about it not being used. But it is not used by CVS; + it exists so one can call it from a debugger. */ +void printlist PROTO ((List *)); + void printlist (list) List *list; diff --git a/contrib/cvs/src/hash.h b/contrib/cvs/src/hash.h index dd83665..a22bc10 100644 --- a/contrib/cvs/src/hash.h +++ b/contrib/cvs/src/hash.h @@ -1,5 +1,3 @@ -/* $CVSid: @(#)hash.h 1.23 94/10/07 $ */ - /* * Copyright (c) 1992, Brian Berliner and Jeff Polk * @@ -20,7 +18,7 @@ enum ntype { UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE, RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE, FILEATTR, - VARIABLE + VARIABLE, RCSFIELD }; typedef enum ntype Ntype; diff --git a/contrib/cvs/src/history.c b/contrib/cvs/src/history.c index 81c71ff..7d28e81 100644 --- a/contrib/cvs/src/history.c +++ b/contrib/cvs/src/history.c @@ -17,6 +17,7 @@ * X is a single character showing the type of event: * T "Tag" cmd. * O "Checkout" cmd. + * E "Export" cmd. * F "Release" cmd. * W "Update" cmd - No User file, Remove from Entries file. * U "Update" cmd - File was checked out over User file. @@ -40,18 +41,18 @@ * command was typed. * T "A" --> New Tag, "D" --> Delete Tag * Otherwise it is the Tag or Date to modify. - * O,F A "" (null field) + * O,F,E A "" (null field) * * rev(s) Revision number or tag. * T The Tag to apply. - * O The Tag or Date, if specified, else "" (null field). + * O,E The Tag or Date, if specified, else "" (null field). * F "" (null field) * W The Tag or Date, if specified, else "" (null field). * U The Revision checked out over the User file. * G,C The Revision(s) involved in merge. * M,A,R RCS Revision affected. * - * argument The module (for [TOUF]) or file (for [WUGCMAR]) affected. + * argument The module (for [TOEUF]) or file (for [WUGCMAR]) affected. * * *** Report categories: "User" and "Since" modifiers apply to all reports. @@ -59,7 +60,7 @@ * * Extract list of record types * - * -e, -x [TOFWUGCMAR] + * -e, -x [TOEFWUGCMAR] * * Extracted records are simply printed, No analysis is performed. * All "field" modifiers apply. -e chooses all types. @@ -177,6 +178,7 @@ */ #include "cvs.h" +#include "savecwd.h" static struct hrec { @@ -206,7 +208,7 @@ static void save_file PROTO((char *dir, char *name, char *module)); static void save_module PROTO((char *module)); static void save_user PROTO((char *name)); -#define ALL_REC_TYPES "TOFWUCGMAR" +#define ALL_REC_TYPES "TOEFWUCGMAR" #define USER_INCREMENT 2 #define FILE_INCREMENT 128 #define MODULE_INCREMENT 5 @@ -236,13 +238,24 @@ static char *tz_name = "+0000"; static char tz_name[] = "LT"; #endif +/* -r, -t, or -b options, malloc'd. These are "" if the option in + question is not specified or is overridden by another option. The + main reason for using "" rather than NULL is historical. Together + with since_date, these are a mutually exclusive set; one overrides the + others. */ +static char *since_rev; +static char *since_tag; +static char *backto; +/* -D option, or 0 if not specified. */ static time_t since_date; -static char since_rev[20]; /* Maxrev ~= 99.99.99.999 */ -static char since_tag[64]; + static struct hrec *last_since_tag; -static char backto[128]; static struct hrec *last_backto; -static char rec_types[20]; + +/* Record types to look for, malloc'd. Probably could be statically + allocated, but only if we wanted to check for duplicates more than + we do. */ +static char *rec_types; static int hrec_count; static int hrec_max; @@ -265,6 +278,12 @@ static int mod_count; /* Number of elements used */ static char *histfile; /* Ptr to the history file name */ +/* This is pretty unclear. First of all, separating "flags" vs. + "options" (I think the distinction is that "options" take arguments) + is nonstandard, and not something we do elsewhere in CVS. Second of + all, what does "reports" mean? I think it means that you can only + supply one of those options, but "reports" hardly has that meaning in + a self-explanatory way. */ static const char *const history_usg[] = { "Usage: %s %s [-report] [-flags] [-options args] [files...]\n\n", @@ -273,7 +292,7 @@ static const char *const history_usg[] = " -c Committed (Modified) files\n", " -o Checked out modules\n", " -m <module> Look for specified module (repeatable)\n", - " -x [TOFWUCGMAR] Extract by record type\n", + " -x [TOEFWUCGMAR] Extract by record type\n", " Flags:\n", " -a All users (Default is self)\n", " -e Everything (same as -x, but all record types)\n", @@ -367,13 +386,17 @@ history (argc, argv) char **argv; { int i, c; - char fname[PATH_MAX]; + char *fname; if (argc == -1) usage (history_usg); + since_rev = xstrdup (""); + since_tag = xstrdup (""); + backto = xstrdup (""); + rec_types = xstrdup (""); optind = 1; - while ((c = getopt (argc, argv, "Tacelow?D:b:f:m:n:p:r:t:u:x:X:z:")) != -1) + while ((c = getopt (argc, argv, "+Tacelow?D:b:f:m:n:p:r:t:u:x:X:z:")) != -1) { switch (c) { @@ -391,7 +414,8 @@ history (argc, argv) case 'e': report_count++; extract++; - (void) strcpy (rec_types, ALL_REC_TYPES); + free (rec_types); + rec_types = xstrdup (ALL_REC_TYPES); break; case 'l': /* Find Last file record */ last_entry = 1; @@ -421,13 +445,8 @@ history (argc, argv) *since_rev = *since_tag = '\0'; since_date = 0; } - if (strlen (optarg) >= sizeof (backto)) - { - error (0, 0, "backto truncated to %d bytes", - sizeof (backto) - 1); - optarg[sizeof (backto) - 1] = '\0'; - } - (void) strcpy (backto, optarg); + free (backto); + backto = xstrdup (optarg); break; case 'f': /* For specified file */ save_file ("", optarg, (char *) NULL); @@ -448,7 +467,8 @@ history (argc, argv) *since_tag = *backto = '\0'; since_date = 0; } - (void) strcpy (since_rev, optarg); + free (since_rev); + since_rev = xstrdup (optarg); break; case 't': /* Since specified Tag/Rev */ if (since_date || *since_rev || *backto) @@ -457,7 +477,8 @@ history (argc, argv) *since_rev = *backto = '\0'; since_date = 0; } - (void) strcpy (since_tag, optarg); /* tag */ + free (since_tag); + since_tag = xstrdup (optarg); break; case 'u': /* For specified username */ save_user (optarg); @@ -472,7 +493,8 @@ history (argc, argv) if (!strchr (ALL_REC_TYPES, *cp)) error (1, 0, "%c is not a valid report type", *cp); } - (void) strcpy (rec_types, optarg); + free (rec_types); + rec_types = xstrdup (optarg); break; case 'z': #ifndef HAVE_RCS5 @@ -564,9 +586,9 @@ history (argc, argv) send_arg("-m"); for (mod = mod_list; mod < &mod_list[mod_count]; ++mod) option_with_arg ("-n", *mod); - if (since_rev != NULL) + if (*since_rev) option_with_arg ("-r", since_rev); - if (since_tag != NULL) + if (*since_tag) option_with_arg ("-t", since_tag); for (mod = user_list; mod < &user_list[user_count]; ++mod) option_with_arg ("-u", *mod); @@ -588,7 +610,10 @@ history (argc, argv) if (tag_report) { if (!strchr (rec_types, 'T')) + { + rec_types = xrealloc (rec_types, strlen (rec_types) + 5); (void) strcat (rec_types, "T"); + } } else if (extract) { @@ -597,7 +622,8 @@ history (argc, argv) } else if (modified) { - (void) strcpy (rec_types, "MAR"); + free (rec_types); + rec_types = xstrdup ("MAR"); /* * If the user has not specified a date oriented flag ("Since"), sort * by Repository/file before date. Default is "just" date. @@ -616,7 +642,8 @@ history (argc, argv) } else if (module_report) { - (void) strcpy (rec_types, last_entry ? "OMAR" : ALL_REC_TYPES); + free (rec_types); + rec_types = xstrdup (last_entry ? "OMAR" : ALL_REC_TYPES); module_sort++; repos_sort++; file_sort++; @@ -625,7 +652,8 @@ history (argc, argv) else /* Must be "checkout" or default */ { - (void) strcpy (rec_types, "OF"); + free (rec_types); + rec_types = xstrdup ("OF"); /* See comments in "modified" above */ if (!last_entry && user_list) user_sort++; @@ -639,7 +667,10 @@ history (argc, argv) /* If we're looking back to a Tag value, must consider "Tag" records */ if (*since_tag && !strchr (rec_types, 'T')) + { + rec_types = xrealloc (rec_types, strlen (rec_types) + 5); (void) strcat (rec_types, "T"); + } argc -= c; argv += c; @@ -647,14 +678,23 @@ history (argc, argv) save_file ("", argv[i], (char *) NULL); if (histfile) - (void) strcpy (fname, histfile); + fname = xstrdup (histfile); else - (void) sprintf (fname, "%s/%s/%s", CVSroot, + { + fname = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_HISTORY) + 10); + (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, CVSROOTADM, CVSROOTADM_HISTORY); + } read_hrecs (fname); qsort ((PTR) hrec_head, hrec_count, sizeof (struct hrec), sort_order); report_hrecs (); + free (fname); + free (since_rev); + free (since_tag); + free (backto); + free (rec_types); return (0); } @@ -667,7 +707,8 @@ history_write (type, update_dir, revs, name, repository) char *name; char *repository; { - char fname[PATH_MAX], workdir[PATH_MAX], homedir[PATH_MAX]; + char *fname; + char *workdir; char *username = getcaller (); int fd; char *line; @@ -678,13 +719,16 @@ history_write (type, update_dir, revs, name, repository) if (logoff) /* History is turned off by cmd line switch */ return; - (void) sprintf (fname, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_HISTORY); + fname = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_HISTORY) + 10); + (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_HISTORY); /* turn off history logging if the history file does not exist */ if (!isfile (fname)) { logoff = 1; - return; + goto out; } if (trace) @@ -695,8 +739,8 @@ history_write (type, update_dir, revs, name, repository) fprintf (stderr, "-> fopen(%s,a)\n", fname); #endif if (noexec) - return; - fd = open (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666); + goto out; + fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666); if (fd < 0) error (1, errno, "cannot open history file: %s", fname); @@ -720,13 +764,21 @@ history_write (type, update_dir, revs, name, repository) else { /* Try harder to find a "homedir" */ - if (!getwd (workdir)) - error (1, errno, "can't getwd in history"); - if (chdir (pwdir) < 0) + struct saved_cwd cwd; + char *homedir; + + if (save_cwd (&cwd)) + error_exit (); + + if ( CVS_CHDIR (pwdir) < 0) error (1, errno, "can't chdir(%s)", pwdir); - if (!getwd (homedir)) + homedir = xgetwd (); + if (homedir == NULL) error (1, errno, "can't getwd in %s", pwdir); - (void) chdir (workdir); + + if (restore_cwd (&cwd, NULL)) + error_exit (); + free_cwd (&cwd); i = strlen (homedir); if (!strncmp (CurDir, homedir, i)) @@ -734,6 +786,7 @@ history_write (type, update_dir, revs, name, repository) PrCurDir += i; /* Point to '/' separator */ tilde = "~"; } + free (homedir); } } } @@ -748,6 +801,8 @@ history_write (type, update_dir, revs, name, repository) else update_dir = ""; + workdir = xmalloc (strlen (tilde) + strlen (PrCurDir) + strlen (slash) + + strlen (update_dir) + 10); (void) sprintf (workdir, "%s%s%s%s", tilde, PrCurDir, slash, update_dir); /* @@ -821,6 +876,9 @@ history_write (type, update_dir, revs, name, repository) free (line); if (close (fd) != 0) error (1, errno, "cannot close history file: %s", fname); + free (workdir); + out: + free (fname); } /* @@ -968,7 +1026,7 @@ fill_hrec (line, hr) NEXT_BAR (repos); NEXT_BAR (rev); hr->idx = idx++; - if (strchr ("FOT", *(hr->type))) + if (strchr ("FOET", *(hr->type))) hr->mod = line; NEXT_BAR (file); /* This returns ptr to next line or final '\0' */ @@ -994,7 +1052,7 @@ read_hrecs (fname) struct hrec *hr; struct stat st_buf; - if ((fd = open (fname, O_RDONLY | OPEN_BINARY)) < 0) + if ((fd = CVS_OPEN (fname, O_RDONLY | OPEN_BINARY)) < 0) error (1, errno, "cannot open history file: %s", fname); if (fstat (fd, &st_buf) < 0) @@ -1134,9 +1192,19 @@ select_hrec (hr) { Vers_TS *vers; time_t t; - - vers = Version_TS (hr->repos, (char *) NULL, since_rev, (char *) NULL, - hr->file, 1, 0, (List *) NULL, (RCSNode *) NULL); + struct file_info finfo; + + memset (&finfo, 0, sizeof finfo); + finfo.file = hr->file; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = hr->repos; + finfo.entries = NULL; + finfo.rcs = NULL; + + vers = Version_TS (&finfo, (char *) NULL, since_rev, (char *) NULL, + 1, 0); if (vers->vn_rcs) { if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, (char *) 0, 0)) @@ -1208,7 +1276,7 @@ select_hrec (hr) */ if (!strchr (rec_types, *(hr->type))) return (0); - if (!strchr ("TFO", *(hr->type))) /* Don't bother with "file" if "TFO" */ + if (!strchr ("TFOE", *(hr->type))) /* Don't bother with "file" if "TFOE" */ { if (file_list) /* If file_list is null, accept all */ { @@ -1220,7 +1288,7 @@ select_hrec (hr) * the concatenation of the repository and file from hrec. * 3. Else compare the file_list entry against the hrec file. */ - char cmpfile[PATH_MAX]; + char *cmpfile = NULL; if (*(cp = fl->l_file) == '*') { @@ -1236,8 +1304,12 @@ select_hrec (hr) { if (strchr (cp, '/')) { - (void) sprintf (cp2 = cmpfile, "%s/%s", + cmpfile = xmalloc (strlen (hr->repos) + + strlen (hr->file) + + 10); + (void) sprintf (cmpfile, "%s/%s", hr->repos, hr->file); + cp2 = cmpfile; } else { @@ -1250,6 +1322,8 @@ select_hrec (hr) hr->mod = fl->l_module; break; } + if (cmpfile != NULL) + free (cmpfile); } } if (!count) @@ -1308,7 +1382,7 @@ report_hrecs () hr++; for (count = hrec_count; count--; lr = hr, hr++) { - char repos[PATH_MAX]; + char *repos; if (!count) hr = NULL; @@ -1316,7 +1390,7 @@ report_hrecs () continue; ty = *(lr->type); - (void) strcpy (repos, lr->repos); + repos = xstrdup (lr->repos); if ((cp = strrchr (repos, '/')) != NULL) { if (lr->mod && !strcmp (++cp, lr->mod)) @@ -1334,6 +1408,7 @@ report_hrecs () rev_len = i; if (lr->mod && (i = strlen (lr->mod)) > mod_len) mod_len = i; + free (repos); } /* Walk through hrec array setting "lr" (Last Record) to each element. @@ -1347,7 +1422,8 @@ report_hrecs () */ for (lr = hrec_head, hr = (lr + 1); hrec_count--; lr = hr, hr++) { - char workdir[PATH_MAX], repos[PATH_MAX]; + char *workdir; + char *repos; if (!hrec_count) hr = NULL; @@ -1368,6 +1444,7 @@ report_hrecs () tm->tm_mday, tm->tm_hour, tm->tm_min, tz_name, user_len, lr->user); + workdir = xmalloc (strlen (lr->dir) + strlen (lr->end) + 10); (void) sprintf (workdir, "%s%s", lr->dir, lr->end); if ((cp = strrchr (workdir, '/')) != NULL) { @@ -1376,6 +1453,7 @@ report_hrecs () (void) strcpy (cp, "*"); } } + repos = xmalloc (strlen (lr->repos) + 10); (void) strcpy (repos, lr->repos); if ((cp = strrchr (repos, '/')) != NULL) { @@ -1395,11 +1473,13 @@ report_hrecs () (void) printf (" {%s}", workdir); break; case 'F': + case 'E': case 'O': if (lr->rev && *(lr->rev)) (void) printf (" [%s]", lr->rev); (void) printf (" %-*s =%s%-*s %s", repos_len, repos, lr->mod, - mod_len + 1 - strlen (lr->mod), "=", workdir); + mod_len + 1 - (int) strlen (lr->mod), + "=", workdir); break; case 'W': case 'U': @@ -1417,6 +1497,8 @@ report_hrecs () break; } (void) putchar ('\n'); + free (workdir); + free (repos); } } diff --git a/contrib/cvs/src/ignore.c b/contrib/cvs/src/ignore.c index 25c2269..05e2f1f 100644 --- a/contrib/cvs/src/ignore.c +++ b/contrib/cvs/src/ignore.c @@ -1,8 +1,19 @@ +/* This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + /* * .cvsignore file support contributed by David G. Grubbs <dgg@odi.com> */ #include "cvs.h" +#include "getline.h" /* * Ignore file section. @@ -19,7 +30,7 @@ static int ign_count; /* Number of active entries */ static int s_ign_count = 0; static int ign_size; /* This many slots available (plus * one for a NULL) */ -static int ign_hold; /* Index where first "temporary" item +static int ign_hold = -1; /* Index where first "temporary" item * is held */ const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\ @@ -42,8 +53,7 @@ int ign_inhibit_server; void ign_setup () { - struct passwd *pw; - char file[PATH_MAX]; + char *home_dir; char *tmp; ign_inhibit_server = 0; @@ -61,17 +71,23 @@ ign_setup () if (!client_active) #endif { + char *file = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_IGNORE) + 10); /* Then add entries found in repository, if it exists */ - (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, - CVSROOTADM_IGNORE); + (void) sprintf (file, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_IGNORE); ign_add_file (file, 0); + free (file); } /* Then add entries found in home dir, (if user has one) and file exists */ - if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir) + home_dir = get_homedir (); + if (home_dir) { - (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTIGNORE); + char *file = xmalloc (strlen (home_dir) + sizeof (CVSDOTIGNORE) + 10); + (void) sprintf (file, "%s/%s", home_dir, CVSDOTIGNORE); ign_add_file (file, 0); + free (file); } /* Then add entries found in CVSIGNORE environment variable. */ @@ -91,7 +107,8 @@ ign_add_file (file, hold) int hold; { FILE *fp; - char line[1024]; + char *line = NULL; + size_t line_allocated = 0; /* restore the saved list (if any) */ if (s_ign_list != NULL) @@ -112,7 +129,7 @@ ign_add_file (file, hold) if (hold) { /* re-set if we had already done a temporary file */ - if (ign_hold) + if (ign_hold >= 0) { int i; @@ -128,17 +145,20 @@ ign_add_file (file, hold) } /* load the file */ - fp = fopen (file, "r"); + fp = CVS_FOPEN (file, "r"); if (fp == NULL) { if (! existence_error (errno)) error (0, errno, "cannot open %s", file); return; } - while (fgets (line, sizeof (line), fp)) + while (getline (&line, &line_allocated, fp) >= 0) ign_add (line, hold); + if (ferror (fp)) + error (0, errno, "cannot read %s", file); if (fclose (fp) < 0) error (0, errno, "cannot close %s", file); + free (line); } /* Parse a line of space-separated wildcards and add them to the list. */ @@ -188,11 +208,11 @@ ign_add (ign, hold) /* temporarily reset the ignore list */ int i; - if (ign_hold) + if (ign_hold >= 0) { for (i = ign_hold; i < ign_count; i++) free (ign_list[i]); - ign_hold = 0; + ign_hold = -1; } s_ign_list = (char **) xmalloc (ign_count * sizeof (char *)); for (i = 0; i < ign_count; i++) @@ -230,8 +250,9 @@ ign_add (ign, hold) } } -/* Set to 1 if ignore file patterns should be matched in a case-insensitive - fashion. */ +/* Set to 1 if filenames should be matched in a case-insensitive + fashion. Note that, contrary to the name and placement in ignore.c, + this is no longer just for ignore patterns. */ int ign_case; /* Return 1 if the given filename should be ignored by update or import. */ @@ -327,29 +348,45 @@ int ignore_directory (name) } /* - * Process the current directory, looking for files not in ILIST and not on - * the global ignore list for this directory. If we find one, call PROC - * passing it the name of the file and the update dir. + * Process the current directory, looking for files not in ILIST and + * not on the global ignore list for this directory. If we find one, + * call PROC passing it the name of the file and the update dir. + * ENTRIES is the entries list, which is used to identify known + * directories. ENTRIES may be NULL, in which case we assume that any + * directory with a CVS administration directory is known. */ void -ignore_files (ilist, update_dir, proc) +ignore_files (ilist, entries, update_dir, proc) List *ilist; + List *entries; char *update_dir; Ignore_proc proc; { + int subdirs; DIR *dirp; struct dirent *dp; struct stat sb; char *file; char *xdir; + /* Set SUBDIRS if we have subdirectory information in ENTRIES. */ + if (entries == NULL) + subdirs = 0; + else + { + struct stickydirtag *sdtp; + + sdtp = (struct stickydirtag *) entries->list->data; + subdirs = sdtp == NULL || sdtp->subdirs; + } + /* we get called with update_dir set to "." sometimes... strip it */ if (strcmp (update_dir, ".") == 0) xdir = ""; else xdir = update_dir; - dirp = opendir ("."); + dirp = CVS_OPENDIR ("."); if (dirp == NULL) return; @@ -363,6 +400,34 @@ ignore_files (ilist, update_dir, proc) continue; if (findnode_fn (ilist, file) != NULL) continue; + if (subdirs) + { + Node *node; + + node = findnode_fn (entries, file); + if (node != NULL + && ((Entnode *) node->data)->type == ENT_SUBDIR) + { + char *p; + int dir; + + /* For consistency with past behaviour, we only ignore + this directory if there is a CVS subdirectory. + This will normally be the case, but the user may + have messed up the working directory somehow. */ + p = xmalloc (strlen (file) + sizeof CVSADM + 10); + sprintf (p, "%s/%s", file, CVSADM); + dir = isdir (p); + free (p); + if (dir) + continue; + } + } + + /* We could be ignoring FIFOs and other files which are neither + regular files nor directories here. */ + if (ign_name (file)) + continue; if ( #ifdef DT_DIR @@ -377,11 +442,19 @@ ignore_files (ilist, update_dir, proc) #endif S_ISDIR(sb.st_mode)) { - char temp[PATH_MAX]; - - (void) sprintf (temp, "%s/%s", file, CVSADM); - if (isdir (temp)) - continue; + if (! subdirs) + { + char *temp; + + temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10); + (void) sprintf (temp, "%s/%s", file, CVSADM); + if (isdir (temp)) + { + free (temp); + continue; + } + free (temp); + } } #ifdef S_ISLNK else if ( @@ -395,10 +468,6 @@ ignore_files (ilist, update_dir, proc) #endif } - /* We could be ignoring FIFOs and other files which are neither - regular files nor directories here. */ - if (ign_name (file)) - continue; (*proc) (file, xdir); } (void) closedir (dirp); diff --git a/contrib/cvs/src/import.c b/contrib/cvs/src/import.c index 98c1635..a37bd4d 100644 --- a/contrib/cvs/src/import.c +++ b/contrib/cvs/src/import.c @@ -25,8 +25,9 @@ static char *get_comment PROTO((char *user)); static int add_rcs_file PROTO((char *message, char *rcs, char *user, char *vtag, int targc, char *targv[])); static int expand_at_signs PROTO((char *buf, off_t size, FILE *fp)); -static int add_rev PROTO((char *message, char *rcs, char *vfile, char *vers)); -static int add_tags PROTO((char *rcs, char *vfile, char *vtag, int targc, +static int add_rev PROTO((char *message, RCSNode *rcs, char *vfile, + char *vers)); +static int add_tags PROTO((RCSNode *rcs, char *vfile, char *vtag, int targc, char *targv[])); static int import_descend PROTO((char *message, char *vtag, int targc, char *targv[])); static int import_descend_dir PROTO((char *message, char *dir, char *vtag, @@ -38,10 +39,10 @@ static int update_rcs_file PROTO((char *message, char *vfile, char *vtag, int ta static void add_log PROTO((int ch, char *fname)); static int repos_len; -static char vhead[50]; -static char vbranch[50]; +static char *vhead; +static char *vbranch; static FILE *logfp; -static char repository[PATH_MAX]; +static char *repository; static int conflicts; static int use_file_modtime; static char *keyword_opt = NULL; @@ -65,11 +66,12 @@ import (argc, argv) char **argv; { char *message = NULL; - char tmpfile[L_tmpnam+1]; + char *tmpfile; char *cp; int i, c, msglen, err; List *ulist; Node *p; + struct logfile_info *li; if (argc == -1) usage (import_usage); @@ -77,9 +79,9 @@ import (argc, argv) ign_setup (); wrap_setup (); - (void) strcpy (vbranch, CVSBRANCH); + vbranch = xstrdup (CVSBRANCH); optind = 1; - while ((c = getopt (argc, argv, "Qqdb:m:I:k:W:")) != -1) + while ((c = getopt (argc, argv, "+Qqdb:m:I:k:W:")) != -1) { switch (c) { @@ -98,7 +100,8 @@ import (argc, argv) use_file_modtime = 1; break; case 'b': - (void) strcpy (vbranch, optarg); + free (vbranch); + vbranch = xstrdup (optarg); break; case 'm': #ifdef FORCE_USE_EDITOR @@ -115,7 +118,7 @@ import (argc, argv) /* RCS_check_kflag returns strings of the form -kxx. We only use it for validation, so we can free the value as soon as it is returned. */ - free (RCS_check_kflag(optarg)); + free (RCS_check_kflag (optarg)); keyword_opt = optarg; break; case 'W': @@ -133,22 +136,32 @@ import (argc, argv) usage (import_usage); for (i = 1; i < argc; i++) /* check the tags for validity */ + { + int j; + RCS_check_tag (argv[i]); + for (j = 1; j < i; j++) + if (strcmp (argv[j], argv[i]) == 0) + error (1, 0, "tag `%s' was specified more than once", argv[i]); + } /* XXX - this should be a module, not just a pathname */ if (! isabsolute (argv[0])) { - if (CVSroot == NULL) + if (CVSroot_directory == NULL) { error (0, 0, "missing CVSROOT environment variable\n"); error (1, 0, "Set it or specify the '-d' option to %s.", program_name); } - (void) sprintf (repository, "%s/%s", CVSroot, argv[0]); - repos_len = strlen (CVSroot); + repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) + + 10); + (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); + repos_len = strlen (CVSroot_directory); } else { + repository = xmalloc (strlen (argv[0]) + 5); (void) strcpy (repository, argv[0]); repos_len = 0; } @@ -164,16 +177,15 @@ import (argc, argv) error (1, 0, "%s is not a numeric branch", vbranch); if (numdots (vbranch) != 2) error (1, 0, "Only branches with two dots are supported: %s", vbranch); - (void) strcpy (vhead, vbranch); + vhead = xstrdup (vbranch); cp = strrchr (vhead, '.'); *cp = '\0'; #ifdef CLIENT_SUPPORT if (client_active) { - /* Do this now; don't ask for a log message if we can't talk to the - server. But if there is a syntax error in the options, give - an error message without connecting. */ + /* For rationale behind calling start_server before do_editor, see + commit.c */ start_server (); } #endif @@ -181,9 +193,9 @@ import (argc, argv) if (use_editor) { do_editor ((char *) NULL, &message, repository, - (List *) NULL); + (List *) NULL); } - + do_verify (message, repository); msglen = message == NULL ? 0 : strlen (message); if (msglen == 0 || message[msglen - 1] != '\n') { @@ -219,6 +231,7 @@ import (argc, argv) send_arg ("-I"); send_arg ("!"); } + wrap_send (); { int i; @@ -244,9 +257,10 @@ import (argc, argv) make_directories (repository); /* Create the logfile that will be logged upon completion */ - if ((logfp = fopen (tmpnam (tmpfile), "w+")) == NULL) + tmpfile = cvs_temp_name (); + if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL) error (1, errno, "cannot create temporary file `%s'", tmpfile); - (void) unlink (tmpfile); /* to be sure it goes away */ + (void) CVS_UNLINK (tmpfile); /* to be sure it goes away */ (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]); (void) fprintf (logfp, "Release Tags:\t"); for (i = 2; i < argc; i++) @@ -298,18 +312,26 @@ import (argc, argv) p->type = UPDATE; p->delproc = update_delproc; p->key = xstrdup ("- Imported sources"); - p->data = (char *) T_TITLE; + li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); + li->type = T_TITLE; + li->tag = xstrdup (vbranch); + li->rev_old = li->rev_new = NULL; + p->data = (char *) li; (void) addnode (ulist, p); - Update_Logfile (repository, message, vbranch, logfp, ulist); + Update_Logfile (repository, message, logfp, ulist); dellist (&ulist); (void) fclose (logfp); /* Make sure the temporary file goes away, even on systems that don't let you delete a file that's in use. */ - unlink (tmpfile); + CVS_UNLINK (tmpfile); + free (tmpfile); if (message) free (message); + free (repository); + free (vbranch); + free (vhead); return (err); } @@ -333,7 +355,7 @@ import_descend (message, vtag, targc, targv) ign_add_file (CVSDOTIGNORE, 1); wrap_add_file (CVSDOTWRAPPER, 1); - if ((dirp = opendir (".")) == NULL) + if ((dirp = CVS_OPENDIR (".")) == NULL) { err++; } @@ -365,7 +387,7 @@ import_descend (message, vtag, targc, targv) #endif && !wrap_name_has (dp->d_name, WRAP_TOCVS) ) - { + { Node *n; if (dirlist == NULL) @@ -377,7 +399,7 @@ import_descend (message, vtag, targc, targv) } else if ( #ifdef DT_DIR - dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && + dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && #endif islink (dp->d_name)) { @@ -389,8 +411,10 @@ import_descend (message, vtag, targc, targv) #ifdef CLIENT_SUPPORT if (client_active) err += client_process_import_file (message, dp->d_name, - vtag, targc, targv, - repository); + vtag, targc, targv, + repository, + keyword_opt != NULL && + keyword_opt[0] == 'b'); else #endif err += process_import_file (message, dp->d_name, @@ -400,7 +424,7 @@ import_descend (message, vtag, targc, targv) (void) closedir (dirp); } - if (dirlist != NULL) + if (dirlist != NULL) { Node *head, *p; @@ -427,28 +451,39 @@ process_import_file (message, vfile, vtag, targc, targv) int targc; char *targv[]; { - char attic_name[PATH_MAX]; - char rcs[PATH_MAX]; + char *rcs; int inattic = 0; + rcs = xmalloc (strlen (repository) + strlen (vfile) + sizeof (RCSEXT) + + 5); (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT); if (!isfile (rcs)) { + char *attic_name; + + attic_name = xmalloc (strlen (repository) + strlen (vfile) + + sizeof (CVSATTIC) + sizeof (RCSEXT) + 10); (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC, vfile, RCSEXT); if (!isfile (attic_name)) { + int retval; + free (attic_name); /* * A new import source file; it doesn't exist as a ,v within the * repository nor in the Attic -- create it anew. */ add_log ('N', vfile); - return (add_rcs_file (message, rcs, vfile, vtag, targc, targv)); + retval = add_rcs_file (message, rcs, vfile, vtag, targc, targv); + free (rcs); + return retval; } + free (attic_name); inattic = 1; } + free (rcs); /* * an rcs file exists. have to do things the official, slow, way. */ @@ -470,24 +505,23 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) { Vers_TS *vers; int letter; - int ierrno; - char *tmpdir; char *tocvsPath; - - vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile, - 1, 0, (List *) NULL, (RCSNode *) NULL); + struct file_info finfo; + + memset (&finfo, 0, sizeof finfo); + finfo.file = vfile; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = repository; + finfo.entries = NULL; + finfo.rcs = NULL; + vers = Version_TS (&finfo, (char *) NULL, vbranch, (char *) NULL, + 1, 0); if (vers->vn_rcs != NULL && !RCS_isdead(vers->srcfile, vers->vn_rcs)) { - char xtmpfile[PATH_MAX]; int different; - int retcode = 0; - - tmpdir = getenv ("TMPDIR"); - if (tmpdir == NULL || tmpdir[0] == '\0') - tmpdir = "/tmp"; - - (void) sprintf (xtmpfile, "%s/cvs-imp%ld", tmpdir, (long) getpid()); /* * The rcs file does have a revision on the vendor branch. Compare @@ -498,35 +532,14 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) * This is to try to cut down the number of "C" conflict messages for * locally modified import source files. */ - /* Why is RCS_FLAGS_FORCE here? I wouldn't think that it would have any - effect in conjunction with passing NULL for workfile (i.e. to stdout). */ - retcode = RCS_checkout (vers->srcfile->path, NULL, vers->vn_rcs, -#ifdef HAVE_RCS5 - "-ko", -#else - NULL, -#endif - xtmpfile, RCS_FLAGS_FORCE, 0); - if (retcode != 0) - { - ierrno = errno; - fperror (logfp, 0, retcode == -1 ? ierrno : 0, - "ERROR: cannot co revision %s of file %s", vers->vn_rcs, - vers->srcfile->path); - error (0, retcode == -1 ? ierrno : 0, - "ERROR: cannot co revision %s of file %s", vers->vn_rcs, - vers->srcfile->path); - (void) unlink_file (xtmpfile); - return (1); - } - tocvsPath = wrap_tocvs_process_file (vfile); - different = xcmp (xtmpfile, vfile); + /* FIXME: Why don't we pass tocvsPath to RCS_cmp_file if it is + not NULL? */ + different = RCS_cmp_file (vers->srcfile, vers->vn_rcs, "-ko", vfile); if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); - (void) unlink_file (xtmpfile); if (!different) { int retval = 0; @@ -536,7 +549,7 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) * "U", signifying that the file has changed, but needs no * attention, and we're done. */ - if (add_tags (vers->srcfile->path, vfile, vtag, targc, targv)) + if (add_tags (vers->srcfile, vfile, vtag, targc, targv)) retval = 1; add_log ('U', vfile); freevers_ts (&vers); @@ -546,8 +559,8 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) /* We may have failed to parse the RCS file; check just in case */ if (vers->srcfile == NULL || - add_rev (message, vers->srcfile->path, vfile, vers->vn_rcs) || - add_tags (vers->srcfile->path, vfile, vtag, targc, targv)) + add_rev (message, vers->srcfile, vfile, vers->vn_rcs) || + add_tags (vers->srcfile, vfile, vtag, targc, targv)) { freevers_ts (&vers); return (1); @@ -573,7 +586,7 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) static int add_rev (message, rcs, vfile, vers) char *message; - char *rcs; + RCSNode *rcs; char *vfile; char *vers; { @@ -619,11 +632,10 @@ add_rev (message, rcs, vfile, vers) } } - status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath, + status = RCS_checkin (rcs->path, tocvsPath == NULL ? vfile : tocvsPath, message, vbranch, (RCS_FLAGS_QUIET - | (use_file_modtime ? RCS_FLAGS_MODTIME : 0)), - 0); + | (use_file_modtime ? RCS_FLAGS_MODTIME : 0))); ierrno = errno; if (tocvsPath == NULL) @@ -636,8 +648,10 @@ add_rev (message, rcs, vfile, vers) { if (!noexec) { - fperror (logfp, 0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs); - error (0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs); + fperror (logfp, 0, status == -1 ? ierrno : 0, + "ERROR: Check-in of %s failed", rcs->path); + error (0, status == -1 ? ierrno : 0, + "ERROR: Check-in of %s failed", rcs->path); } if (locked) { @@ -656,7 +670,7 @@ add_rev (message, rcs, vfile, vers) */ static int add_tags (rcs, vfile, vtag, targc, targv) - char *rcs; + RCSNode *rcs; char *vfile; char *vtag; int targc; @@ -665,6 +679,7 @@ add_tags (rcs, vfile, vtag, targc, targv) int i, ierrno; Vers_TS *vers; int retcode = 0; + struct file_info finfo; if (noexec) return (0); @@ -672,23 +687,33 @@ add_tags (rcs, vfile, vtag, targc, targv) if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0) { ierrno = errno; - fperror (logfp, 0, retcode == -1 ? ierrno : 0, - "ERROR: Failed to set tag %s in %s", vtag, rcs); + fperror (logfp, 0, retcode == -1 ? ierrno : 0, + "ERROR: Failed to set tag %s in %s", vtag, rcs->path); error (0, retcode == -1 ? ierrno : 0, - "ERROR: Failed to set tag %s in %s", vtag, rcs); + "ERROR: Failed to set tag %s in %s", vtag, rcs->path); return (1); } - vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile, - 1, 0, (List *) NULL, (RCSNode *) NULL); + + memset (&finfo, 0, sizeof finfo); + finfo.file = vfile; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = repository; + finfo.entries = NULL; + finfo.rcs = NULL; + vers = Version_TS (&finfo, NULL, vtag, NULL, 1, 0); for (i = 0; i < targc; i++) { if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) != 0) { ierrno = errno; - fperror (logfp, 0, retcode == -1 ? ierrno : 0, - "WARNING: Couldn't add tag %s to %s", targv[i], rcs); + fperror (logfp, 0, retcode == -1 ? ierrno : 0, + "WARNING: Couldn't add tag %s to %s", targv[i], + rcs->path); error (0, retcode == -1 ? ierrno : 0, - "WARNING: Couldn't add tag %s to %s", targv[i], rcs); + "WARNING: Couldn't add tag %s to %s", targv[i], + rcs->path); } } freevers_ts (&vers); @@ -712,6 +737,10 @@ static const struct compair comtable[] = * 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. + * + * I believe that the comment leader is unused if you are using RCS 5.7, which + * decides what leader to use based on the text surrounding the $Log keyword + * rather than a specified comment leader. */ {"a", "-- "}, /* Ada */ {"ada", "-- "}, @@ -806,9 +835,11 @@ get_comment (user) char *user; { char *cp, *suffix; - char suffix_path[PATH_MAX]; + char *suffix_path; int i; + char *retval; + suffix_path = xmalloc (strlen (user) + 5); cp = strrchr (user, '.'); if (cp != NULL) { @@ -828,11 +859,21 @@ get_comment (user) suffix = ""; /* will use the default */ for (i = 0;; i++) { - if (comtable[i].suffix == NULL) /* default */ - return (comtable[i].comlead); + if (comtable[i].suffix == NULL) + { + /* Default. Note we'll always hit this case before we + ever return NULL. */ + retval = comtable[i].comlead; + break; + } if (strcmp (suffix, comtable[i].suffix) == 0) - return (comtable[i].comlead); + { + retval = comtable[i].comlead; + break; + } } + free (suffix_path); + return retval; } static int @@ -848,26 +889,32 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) struct stat sb; struct tm *ftm; time_t now; - char altdate1[50]; + char altdate1[MAXDATELEN]; #ifndef HAVE_RCS5 - char altdate2[50]; + char altdate2[MAXDATELEN]; #endif char *author; int i, ierrno, err = 0; mode_t mode; char *tocvsPath; char *userfile; + char *local_opt = keyword_opt; + char *free_opt = NULL; if (noexec) return (0); - /* FIXME? We always import files as text files (note that means - that files get stored with straight linefeeds). There isn't an - obvious, clean, way to let people specify which files are binary. - Maybe based on the file name.... */ + if (local_opt == NULL) + { + if (wrap_name_has (user, WRAP_RCSOPTION)) + { + local_opt = free_opt = wrap_rcsoption (user, 0); + } + } + tocvsPath = wrap_tocvs_process_file (user); userfile = (tocvsPath == NULL ? user : tocvsPath); - fpuser = fopen (userfile, "r"); + fpuser = CVS_FOPEN (userfile, "r"); if (fpuser == NULL) { /* not fatal, continue import */ @@ -875,7 +922,7 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) error (0, errno, "ERROR: cannot read file %s", userfile); goto read_error; } - fprcs = fopen (rcs, "w+b"); + fprcs = CVS_FOPEN (rcs, "w+b"); if (fprcs == NULL) { ierrno = errno; @@ -905,11 +952,13 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) goto write_error; } - if (keyword_opt != NULL) - if (fprintf (fprcs, "expand @%s@;\012", keyword_opt) < 0) + if (local_opt != NULL) + { + if (fprintf (fprcs, "expand @%s@;\012", local_opt) < 0) { - goto write_error; + goto write_error; } + } if (fprintf (fprcs, "\012") < 0) goto write_error; @@ -976,7 +1025,7 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) /* Now copy over the contents of the file, expanding at signs. */ { - unsigned char buf[8192]; + char buf[8192]; unsigned int len; while (1) @@ -1030,6 +1079,8 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); + if (free_opt != NULL) + free (free_opt); return (err); write_error: @@ -1041,7 +1092,7 @@ write_error_noclose: error (0, ierrno, "ERROR: cannot write file %s", rcs); if (ierrno == ENOSPC) { - (void) unlink (rcs); + (void) CVS_UNLINK (rcs); fperror (logfp, 0, 0, "ERROR: out of space - aborting"); error (1, 0, "ERROR: out of space - aborting"); } @@ -1050,6 +1101,9 @@ read_error: if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); + if (free_opt != NULL) + free (free_opt); + return (err + 1); } @@ -1135,6 +1189,7 @@ import_descend_dir (message, dir, vtag, targc, targv) struct saved_cwd cwd; char *cp; int ierrno, err; + char *rcs = NULL; if (islink (dir)) return (0); @@ -1143,13 +1198,24 @@ import_descend_dir (message, dir, vtag, targc, targv) fperror (logfp, 0, 0, "ERROR: cannot get working directory"); return (1); } + + /* Concatenate DIR to the end of REPOSITORY. */ if (repository[0] == '\0') - (void) strcpy (repository, dir); + { + char *new = xstrdup (dir); + free (repository); + repository = new; + } else { - (void) strcat (repository, "/"); - (void) strcat (repository, dir); + char *new = xmalloc (strlen (repository) + strlen (dir) + 10); + strcpy (new, repository); + (void) strcat (new, "/"); + (void) strcat (new, dir); + free (repository); + repository = new; } + #ifdef CLIENT_SUPPORT if (!quiet && !client_active) #else @@ -1157,7 +1223,7 @@ import_descend_dir (message, dir, vtag, targc, targv) #endif error (0, 0, "Importing %s", repository); - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) { ierrno = errno; fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository); @@ -1171,12 +1237,12 @@ import_descend_dir (message, dir, vtag, targc, targv) if (!isdir (repository)) #endif { - char rcs[PATH_MAX]; - + rcs = xmalloc (strlen (repository) + sizeof (RCSEXT) + 5); (void) sprintf (rcs, "%s%s", repository, RCSEXT); if (isfile (repository) || isfile(rcs)) { - fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!", + fperror (logfp, 0, 0, + "ERROR: %s is a file, should be a directory!", repository); error (0, 0, "ERROR: %s is a file, should be a directory!", repository); @@ -1196,12 +1262,14 @@ import_descend_dir (message, dir, vtag, targc, targv) } err = import_descend (message, vtag, targc, targv); out: + if (rcs != NULL) + free (rcs); if ((cp = strrchr (repository, '/')) != NULL) *cp = '\0'; else repository[0] = '\0'; if (restore_cwd (&cwd, NULL)) - exit (EXIT_FAILURE); + error_exit (); free_cwd (&cwd); return (err); } diff --git a/contrib/cvs/src/lock.c b/contrib/cvs/src/lock.c index 7e35aed..f84eccf 100644 --- a/contrib/cvs/src/lock.c +++ b/contrib/cvs/src/lock.c @@ -10,40 +10,160 @@ * Lock file support for CVS. */ +/* The node Concurrency in doc/cvs.texinfo has a brief introduction to + how CVS locks function, and some of the user-visible consequences of + their existence. Here is a summary of why they exist (and therefore, + the consequences of hacking CVS to read a repository without creating + locks): + + There are two uses. One is the ability to prevent there from being + two writers at the same time. This is necessary for any number of + reasons (fileattr code, probably others). Commit needs to lock the + whole tree so that nothing happens between the up-to-date check and + the actual checkin. + + The second use is the ability to ensure that there is not a writer + and a reader at the same time (several readers are allowed). Reasons + for this are: + + * Readlocks ensure that once CVS has found a collection of rcs + files using Find_Names, the files will still exist when it reads + them (they may have moved in or out of the attic). + + * Readlocks provide some modicum of consistency, although this is + kind of limited--see the node Concurrency in cvs.texinfo. + + * Readlocks ensure that the RCS file does not change between + RCS_parse and RCS_reparsercsfile time. This one strikes me as + important, although I haven't thought up what bad scenarios might + be. + + * Readlocks ensure that we won't find the file in the state in + which it is in between the "rcs -i" and the RCS_checkin in commit.c + (when a file is being added). This state is a state in which the + RCS file parsing routines in rcs.c cannot parse the file. + + * Readlocks ensure that a reader won't try to look at a + half-written fileattr file (fileattr is not updated atomically). + + (see also the description of anonymous read-only access in + "Password authentication security" node in doc/cvs.texinfo). + + While I'm here, I'll try to summarize a few random suggestions + which periodically get made about how locks might be different: + + 1. Check for EROFS. Maybe useful, although in the presence of NFS + EROFS does *not* mean that the file system is unchanging. + + 2. Provide a means to put the cvs locks in some directory apart from + the repository (CVSROOT/locks; a -l option in modules; etc.). + + 3. Provide an option to disable locks for operations which only + read (see above for some of the consequences). + + 4. Have a server internally do the locking. Probably a good + long-term solution, and many people have been working hard on code + changes which would eventually make it possible to have a server + which can handle various connections in one process, but there is + much, much work still to be done before this is feasible. + + 5. Like #4 but use shared memory or something so that the servers + merely need to all be on the same machine. This is a much smaller + change to CVS (it functions much like #2; shared memory might be an + unneeded complication although it presumably would be faster). */ + #include "cvs.h" +struct lock { + /* This is the directory in which we may have a lock named by the + readlock variable, a lock named by the writelock variable, and/or + a lock named CVSLCK. The storage is not allocated along with the + struct lock; it is allocated by the Reader_Lock caller or in the + case of writelocks, it is just a pointer to the storage allocated + for the ->key field. */ + char *repository; + /* Do we have a lock named CVSLCK? */ + int have_lckdir; + /* Note there is no way of knowing whether the readlock and writelock + exist. The code which sets the locks doesn't use SIG_beginCrSect + to set a flag like we do for CVSLCK. */ +}; + +static void remove_locks PROTO((void)); static int readers_exist PROTO((char *repository)); -static int set_lock PROTO((char *repository, int will_wait)); -static void clear_lock PROTO((void)); +static int set_lock PROTO ((struct lock *lock, int will_wait)); +static void clear_lock PROTO ((struct lock *lock)); static void set_lockers_name PROTO((struct stat *statp)); static int set_writelock_proc PROTO((Node * p, void *closure)); static int unlock_proc PROTO((Node * p, void *closure)); -static int write_lock PROTO((char *repository)); -static void lock_simple_remove PROTO((char *repository)); +static int write_lock PROTO ((struct lock *lock)); +static void lock_simple_remove PROTO ((struct lock *lock)); static void lock_wait PROTO((char *repository)); -static int Check_Owner PROTO((char *lockdir)); - -static char lockers_name[20]; -static char *repository; -static char readlock[PATH_MAX], writelock[PATH_MAX], masterlock[PATH_MAX]; -static int cleanup_lckdir; +static void lock_obtained PROTO((char *repository)); + +/* Malloc'd array containing the username of the whoever has the lock. + Will always be non-NULL in the cases where it is needed. */ +static char *lockers_name; +/* Malloc'd array specifying name of a readlock within a directory. + Or NULL if none. */ +static char *readlock; +/* Malloc'd array specifying name of a writelock within a directory. + Or NULL if none. */ +static char *writelock; +/* Malloc'd array specifying the name of a CVSLCK file (absolute pathname). + Will always be non-NULL in the cases where it is used. */ +static char *masterlock; static List *locklist; #define L_OK 0 /* success */ #define L_ERROR 1 /* error condition */ #define L_LOCKED 2 /* lock owned by someone else */ +/* This is the (single) readlock which is set by Reader_Lock. The + repository field is NULL if there is no such lock. */ +static struct lock global_readlock; + +/* List of locks set by lock_tree_for_write. This is redundant + with locklist, sort of. */ +static List *lock_tree_list; + +/* If we set locks with lock_dir_for_write, then locked_dir contains + the malloc'd name of the repository directory which we have locked. + locked_list is the same thing packaged into a list and is redundant + with locklist the same way that lock_tree_list is. */ +static char *locked_dir; +static List *locked_list; + /* * Clean up all outstanding locks */ void Lock_Cleanup () { + remove_locks (); + + dellist (&lock_tree_list); + + if (locked_dir != NULL) + { + dellist (&locked_list); + free (locked_dir); + locked_dir = NULL; + locked_list = NULL; + } +} + +/* + * Remove locks without discarding the lock information + */ +static void +remove_locks () +{ /* clean up simple locks (if any) */ - if (repository != NULL) + if (global_readlock.repository != NULL) { - lock_simple_remove (repository); - repository = (char *) NULL; + lock_simple_remove (&global_readlock); + global_readlock.repository = NULL; } /* clean up multiple locks (if any) */ @@ -62,87 +182,57 @@ unlock_proc (p, closure) Node *p; void *closure; { - lock_simple_remove (p->key); + lock_simple_remove ((struct lock *)p->data); return (0); } -/* - * Remove the lock files (without complaining if they are not there), - */ +/* Remove the lock files. */ static void -lock_simple_remove (repository) - char *repository; +lock_simple_remove (lock) + struct lock *lock; { - char tmp[PATH_MAX]; + char *tmp; - if (readlock[0] != '\0') + /* If readlock is set, the lock directory *might* have been created, but + since Reader_Lock doesn't use SIG_beginCrSect the way that set_lock + does, we don't know that. That is why we need to check for + existence_error here. */ + if (readlock != NULL) { - (void) sprintf (tmp, "%s/%s", repository, readlock); - if (unlink (tmp) < 0 && ! existence_error (errno)) + tmp = xmalloc (strlen (lock->repository) + strlen (readlock) + 10); + (void) sprintf (tmp, "%s/%s", lock->repository, readlock); + if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); + free (tmp); } - if (writelock[0] != '\0') + /* If writelock is set, the lock directory *might* have been created, but + since write_lock doesn't use SIG_beginCrSect the way that set_lock + does, we don't know that. That is why we need to check for + existence_error here. */ + if (writelock != NULL) { - (void) sprintf (tmp, "%s/%s", repository, writelock); - if (unlink (tmp) < 0 && ! existence_error (errno)) + tmp = xmalloc (strlen (lock->repository) + strlen (writelock) + 10); + (void) sprintf (tmp, "%s/%s", lock->repository, writelock); + if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); + free (tmp); } - /* - * Only remove the lock directory if it is ours, note that this does - * lead to the limitation that one user ID should not be committing - * files into the same Repository directory at the same time. Oh well. - */ - if (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir)) + if (lock->have_lckdir) { - (void) sprintf (tmp, "%s/%s", repository, CVSLCK); - if (Check_Owner(tmp)) - { -#ifdef AFSCVS - char rmuidlock[PATH_MAX]; - sprintf(rmuidlock, "rm -f %s/uidlock%d", tmp, geteuid() ); - system(rmuidlock); -#endif - (void) rmdir (tmp); - } + tmp = xmalloc (strlen (lock->repository) + sizeof (CVSLCK) + 10); + (void) sprintf (tmp, "%s/%s", lock->repository, CVSLCK); + SIG_beginCrSect (); + if (CVS_RMDIR (tmp) < 0) + error (0, errno, "failed to remove lock dir %s", tmp); + lock->have_lckdir = 0; + SIG_endCrSect (); + free (tmp); } - cleanup_lckdir = 0; } /* - * Check the owner of a lock. Returns 1 if we own it, 0 otherwise. - */ -static int -Check_Owner(lockdir) - char *lockdir; -{ - struct stat sb; - -#ifdef AFSCVS - /* In the Andrew File System (AFS), user ids from stat don't match - those from geteuid(). The AFSCVS code can deal with either AFS or - non-AFS repositories; the non-AFSCVS code is faster. */ - char uidlock[PATH_MAX]; - - /* Check if the uidlock is in the lock directory */ - sprintf(uidlock, "%s/uidlock%d", lockdir, geteuid() ); - if( stat(uidlock, &sb) != -1) - return 1; /* The file exists, therefore we own the lock */ - else - return 0; /* The file didn't exist or some other error. - * Assume that we don't own it. - */ -#else - if (stat (lockdir, &sb) != -1 && sb.st_uid == geteuid ()) - return 1; - else - return 0; -#endif -} /* end Check_Owner() */ - - -/* * Create a lock file for readers */ int @@ -151,51 +241,63 @@ Reader_Lock (xrepository) { int err = 0; FILE *fp; - char tmp[PATH_MAX]; + char *tmp; if (noexec) return (0); /* we only do one directory at a time for read locks! */ - if (repository != NULL) + if (global_readlock.repository != NULL) { error (0, 0, "Reader_Lock called while read locks set - Help!"); return (1); } - if (readlock[0] == '\0') - (void) sprintf (readlock, + if (readlock == NULL) + { + readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40); + (void) sprintf (readlock, #ifdef HAVE_LONG_FILE_NAMES - "%s.%s.%ld", CVSRFL, hostname, + "%s.%s.%ld", CVSRFL, hostname, #else - "%s.%ld", CVSRFL, + "%s.%ld", CVSRFL, #endif - (long) getpid ()); + (long) getpid ()); + } - /* remember what we're locking (for lock_cleanup) */ - repository = xrepository; + /* remember what we're locking (for Lock_Cleanup) */ + global_readlock.repository = xrepository; /* get the lock dir for our own */ - if (set_lock (xrepository, 1) != L_OK) + if (set_lock (&global_readlock, 1) != L_OK) { error (0, 0, "failed to obtain dir lock in repository `%s'", xrepository); - readlock[0] = '\0'; + if (readlock != NULL) + free (readlock); + readlock = NULL; + /* We don't set global_readlock.repository to NULL. I think this + only works because recurse.c will give a fatal error if we return + a nonzero value. */ return (1); } /* write a read-lock */ + tmp = xmalloc (strlen (xrepository) + strlen (readlock) + 10); (void) sprintf (tmp, "%s/%s", xrepository, readlock); - if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF) + if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF) { error (0, errno, "cannot create read lock in repository `%s'", xrepository); - readlock[0] = '\0'; + if (readlock != NULL) + free (readlock); + readlock = NULL; err = 1; } + free (tmp); /* free the lock dir */ - clear_lock(); + clear_lock (&global_readlock); return (err); } @@ -205,10 +307,15 @@ Reader_Lock (xrepository) */ static char *lock_error_repos; static int lock_error; -int + +static int Writer_Lock PROTO ((List * list)); + +static int Writer_Lock (list) List *list; { + char *wait_repos; + if (noexec) return (0); @@ -219,32 +326,45 @@ Writer_Lock (list) return (1); } + wait_repos = NULL; for (;;) { /* try to lock everything on the list */ lock_error = L_OK; /* init for set_writelock_proc */ lock_error_repos = (char *) NULL; /* init for set_writelock_proc */ locklist = list; /* init for Lock_Cleanup */ - (void) strcpy (lockers_name, "unknown"); + if (lockers_name != NULL) + free (lockers_name); + lockers_name = xstrdup ("unknown"); (void) walklist (list, set_writelock_proc, NULL); switch (lock_error) { case L_ERROR: /* Real Error */ + if (wait_repos != NULL) + free (wait_repos); Lock_Cleanup (); /* clean up any locks we set */ error (0, 0, "lock failed - giving up"); return (1); case L_LOCKED: /* Someone already had a lock */ - Lock_Cleanup (); /* clean up any locks we set */ + remove_locks (); /* clean up any locks we set */ lock_wait (lock_error_repos); /* sleep a while and try again */ + wait_repos = xstrdup (lock_error_repos); continue; case L_OK: /* we got the locks set */ + if (wait_repos != NULL) + { + lock_obtained (wait_repos); + free (wait_repos); + } return (0); default: + if (wait_repos != NULL) + free (wait_repos); error (0, 0, "unknown lock status %d in Writer_Lock", lock_error); return (1); @@ -266,7 +386,7 @@ set_writelock_proc (p, closure) /* apply the write lock */ lock_error_repos = p->key; - lock_error = write_lock (p->key); + lock_error = write_lock ((struct lock *)p->data); return (0); } @@ -275,33 +395,36 @@ set_writelock_proc (p, closure) * lock held by someone else or L_ERROR if an error occurred */ static int -write_lock (repository) - char *repository; +write_lock (lock) + struct lock *lock; { int status; FILE *fp; - char tmp[PATH_MAX]; + char *tmp; - if (writelock[0] == '\0') + if (writelock == NULL) + { + writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40); (void) sprintf (writelock, #ifdef HAVE_LONG_FILE_NAMES - "%s.%s.%ld", CVSWFL, hostname, + "%s.%s.%ld", CVSWFL, hostname, #else - "%s.%ld", CVSWFL, + "%s.%ld", CVSWFL, #endif - (long) getpid()); + (long) getpid()); + } /* make sure the lock dir is ours (not necessarily unique to us!) */ - status = set_lock (repository, 0); + status = set_lock (lock, 0); if (status == L_OK) { /* we now own a writer - make sure there are no readers */ - if (readers_exist (repository)) + if (readers_exist (lock->repository)) { /* clean up the lock dir if we created it */ if (status == L_OK) { - clear_lock(); + clear_lock (lock); } /* indicate we failed due to read locks instead of error */ @@ -309,25 +432,28 @@ write_lock (repository) } /* write the write-lock file */ - (void) sprintf (tmp, "%s/%s", repository, writelock); - if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF) + tmp = xmalloc (strlen (lock->repository) + strlen (writelock) + 10); + (void) sprintf (tmp, "%s/%s", lock->repository, writelock); + if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF) { int xerrno = errno; - if (unlink (tmp) < 0 && ! existence_error (errno)) + if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); /* free the lock dir if we created it */ if (status == L_OK) { - clear_lock(); + clear_lock (lock); } /* return the error */ error (0, xerrno, "cannot create write lock in repository `%s'", - repository); + lock->repository); + free (tmp); return (L_ERROR); } + free (tmp); return (L_OK); } else @@ -353,7 +479,7 @@ readers_exist (repository) again: #endif - if ((dirp = opendir (repository)) == NULL) + if ((dirp = CVS_OPENDIR (repository)) == NULL) error (1, 0, "cannot open directory %s", repository); errno = 0; @@ -368,7 +494,7 @@ again: line = xmalloc (strlen (repository) + strlen (dp->d_name) + 5); (void) sprintf (line, "%s/%s", repository, dp->d_name); - if (stat (line, &sb) != -1) + if ( CVS_STAT (line, &sb) != -1) { #ifdef CVS_FUDGELOCKS /* @@ -376,7 +502,7 @@ again: * seconds ago, try to clean-up the lock file, and if * successful, re-open the directory and try again. */ - if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1) + if (now >= (sb.st_ctime + CVSLCKAGE) && CVS_UNLINK (line) != -1) { (void) closedir (dirp); free (line); @@ -418,13 +544,18 @@ set_lockers_name (statp) { struct passwd *pw; + if (lockers_name != NULL) + free (lockers_name); if ((pw = (struct passwd *) getpwuid (statp->st_uid)) != (struct passwd *) NULL) { - (void) strcpy (lockers_name, pw->pw_name); + lockers_name = xstrdup (pw->pw_name); } else + { + lockers_name = xmalloc (20); (void) sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid); + } } /* @@ -433,24 +564,29 @@ set_lockers_name (statp) * seconds old, just try to remove the directory. */ static int -set_lock (repository, will_wait) - char *repository; +set_lock (lock, will_wait) + struct lock *lock; int will_wait; { + int waited; struct stat sb; mode_t omask; #ifdef CVS_FUDGELOCKS time_t now; #endif - (void) sprintf (masterlock, "%s/%s", repository, CVSLCK); + if (masterlock != NULL) + free (masterlock); + masterlock = xmalloc (strlen (lock->repository) + sizeof (CVSLCK) + 10); + (void) sprintf (masterlock, "%s/%s", lock->repository, CVSLCK); /* * Note that it is up to the callers of set_lock() to arrange for signal * handlers that do the appropriate things, like remove the lock * directory before they exit. */ - cleanup_lckdir = 0; + waited = 0; + lock->have_lckdir = 0; for (;;) { int status = -1; @@ -458,27 +594,11 @@ set_lock (repository, will_wait) SIG_beginCrSect (); if (CVS_MKDIR (masterlock, 0777) == 0) { -#ifdef AFSCVS - char uidlock[PATH_MAX]; - FILE *fp; - - sprintf(uidlock, "%s/uidlock%d", masterlock, geteuid() ); - if ((fp = fopen(uidlock, "w+")) == NULL) - { - /* We failed to create the uidlock, - so rm masterlock and leave */ - rmdir(masterlock); - SIG_endCrSect (); - status = L_ERROR; - goto out; - } - - /* We successfully created the uid lock, so close the file */ - fclose(fp); -#endif - cleanup_lckdir = 1; + lock->have_lckdir = 1; SIG_endCrSect (); status = L_OK; + if (waited) + lock_obtained (lock->repository); goto out; } SIG_endCrSect (); @@ -491,15 +611,14 @@ set_lock (repository, will_wait) { error (0, errno, "failed to create lock directory in repository `%s'", - repository); + lock->repository); return (L_ERROR); } - /* - * stat the dir - if it is non-existent, re-try the loop since - * someone probably just removed it (thus releasing the lock) - */ - if (stat (masterlock, &sb) < 0) + /* Find out who owns the lock. If the lock directory is + non-existent, re-try the loop since someone probably just + removed it (thus releasing the lock). */ + if (CVS_STAT (masterlock, &sb) < 0) { if (existence_error (errno)) continue; @@ -517,13 +636,7 @@ set_lock (repository, will_wait) (void) time (&now); if (now >= (sb.st_ctime + CVSLCKAGE)) { -#ifdef AFSCVS - /* Remove the uidlock first */ - char rmuidlock[PATH_MAX]; - sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() ); - system(rmuidlock); -#endif - if (rmdir (masterlock) >= 0) + if (CVS_RMDIR (masterlock) >= 0) continue; } #endif @@ -534,7 +647,8 @@ set_lock (repository, will_wait) /* if he wasn't willing to wait, return an error */ if (!will_wait) return (L_LOCKED); - lock_wait (repository); + lock_wait (lock->repository); + waited = 1; } } @@ -543,17 +657,14 @@ set_lock (repository, will_wait) * clear_lock is never called except after a successful set_lock(). */ static void -clear_lock() +clear_lock (lock) + struct lock *lock; { -#ifdef AFSCVS - /* Remove the uidlock first */ - char rmuidlock[PATH_MAX]; - sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() ); - system(rmuidlock); -#endif - if (rmdir (masterlock) < 0) + SIG_beginCrSect (); + if (CVS_RMDIR (masterlock) < 0) error (0, errno, "failed to remove lock dir `%s'", masterlock); - cleanup_lckdir = 0; + lock->have_lckdir = 0; + SIG_endCrSect (); } /* @@ -568,30 +679,54 @@ lock_wait (repos) (void) time (&now); error (0, 0, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11, lockers_name, repos); + /* Call cvs_flusherr to ensure that the user sees this message as + soon as possible. */ + cvs_flusherr (); (void) sleep (CVSLCKSLEEP); } + +/* + * Print out a message when we obtain a lock. + */ +static void +lock_obtained (repos) + char *repos; +{ + time_t now; + + (void) time (&now); + error (0, 0, "[%8.8s] obtained lock in %s", ctime (&now) + 11, repos); + /* Call cvs_flusherr to ensure that the user sees this message as + soon as possible. */ + cvs_flusherr (); +} -static int lock_filesdoneproc PROTO ((int err, char *repository, - char *update_dir)); +static int lock_filesdoneproc PROTO ((void *callerdat, int err, + char *repository, char *update_dir, + List *entries)); static int fsortcmp PROTO((const Node * p, const Node * q)); -static List *lock_tree_list; - /* * Create a list of repositories to lock */ /* ARGSUSED */ static int -lock_filesdoneproc (err, repository, update_dir) +lock_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { Node *p; p = getnode (); p->type = LOCK; p->key = xstrdup (repository); + p->data = xmalloc (sizeof (struct lock)); + ((struct lock *)p->data)->repository = p->key; + ((struct lock *)p->data)->have_lckdir = 0; + /* FIXME-KRP: this error condition should not simply be passed by. */ if (p->key == NULL || addnode (lock_tree_list, p) != 0) freenode (p); @@ -623,17 +758,39 @@ lock_tree_for_write (argc, argv, local, aflag) */ lock_tree_list = getlist (); err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, argc, - argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0, - 0); + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, + argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0); sortlist (lock_tree_list, fsortcmp); if (Writer_Lock (lock_tree_list) != 0) error (1, 0, "lock failed - giving up"); } - + +/* Lock a single directory in REPOSITORY. It is OK to call this if + a lock has been set with lock_dir_for_write; the new lock will replace + the old one. If REPOSITORY is NULL, don't do anything. */ void -lock_tree_cleanup () +lock_dir_for_write (repository) + char *repository; { - Lock_Cleanup (); - dellist (&lock_tree_list); + if (repository != NULL + && (locked_dir == NULL + || strcmp (locked_dir, repository) != 0)) + { + Node *node; + + if (locked_dir != NULL) + Lock_Cleanup (); + + locked_dir = xstrdup (repository); + locked_list = getlist (); + node = getnode (); + node->type = LOCK; + node->key = xstrdup (repository); + node->data = xmalloc (sizeof (struct lock)); + ((struct lock *)node->data)->repository = node->key; + ((struct lock *)node->data)->have_lckdir = 0; + + (void) addnode (locked_list, node); + Writer_Lock (locked_list); + } } diff --git a/contrib/cvs/src/log.c b/contrib/cvs/src/log.c index f167d92..8fb6910 100644 --- a/contrib/cvs/src/log.c +++ b/contrib/cvs/src/log.c @@ -17,42 +17,213 @@ #include "cvs.h" -static Dtype log_dirproc PROTO((char *dir, char *repository, char *update_dir)); -static int log_fileproc PROTO((struct file_info *finfo)); +/* This structure holds information parsed from the -r option. */ + +struct option_revlist +{ + /* The next -r option. */ + struct option_revlist *next; + /* The first revision to print. This is NULL if the range is + :rev, or if no revision is given. */ + char *first; + /* The last revision to print. This is NULL if the range is rev:, + or if no revision is given. If there is no colon, first and + last are the same. */ + char *last; + /* Nonzero if there was a trailing `.', which means to print only + the head revision of a branch. */ + int branchhead; +}; + +/* This structure holds information derived from option_revlist given + a particular RCS file. */ + +struct revlist +{ + /* The next pair. */ + struct revlist *next; + /* The first numeric revision to print. */ + char *first; + /* The last numeric revision to print. */ + char *last; + /* The number of fields in these revisions (one more than + numdots). */ + int fields; +}; + +/* This structure holds information parsed from the -d option. */ + +struct datelist +{ + /* The next date. */ + struct datelist *next; + /* The starting date. */ + char *start; + /* The ending date. */ + char *end; + /* Nonzero if the range is inclusive rather than exclusive. */ + int inclusive; +}; + +/* This structure is used to pass information through start_recursion. */ +struct log_data +{ + /* Nonzero if the -R option was given, meaning that only the name + of the RCS file should be printed. */ + int nameonly; + /* Nonzero if the -h option was given, meaning that only header + information should be printed. */ + int header; + /* Nonzero if the -t option was given, meaning that only the + header and the descriptive text should be printed. */ + int long_header; + /* Nonzero if the -N option was seen, meaning that tag information + should not be printed. */ + int notags; + /* Nonzero if the -b option was seen, meaning that only revisions + on the default branch should be printed. */ + int default_branch; + /* If not NULL, the value given for the -r option, which lists + sets of revisions to be printed. */ + struct option_revlist *revlist; + /* If not NULL, the date pairs given for the -d option, which + select date ranges to print. */ + struct datelist *datelist; + /* If not NULL, the single dates given for the -d option, which + select specific revisions to print based on a date. */ + struct datelist *singledatelist; + /* If not NULL, the list of states given for the -s option, which + only prints revisions of given states. */ + List *statelist; + /* If not NULL, the list of login names given for the -w option, + which only prints revisions checked in by given users. */ + List *authorlist; +}; + +/* This structure is used to pass information through walklist. */ +struct log_data_and_rcs +{ + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; +}; + +static Dtype log_dirproc PROTO ((void *callerdat, char *dir, + char *repository, char *update_dir, + List *entries)); +static int log_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static struct option_revlist *log_parse_revlist PROTO ((const char *)); +static void log_parse_date PROTO ((struct log_data *, const char *)); +static void log_parse_list PROTO ((List **, const char *)); +static struct revlist *log_expand_revlist PROTO ((RCSNode *, + struct option_revlist *, + int)); +static void log_free_revlist PROTO ((struct revlist *)); +static int log_version_requested PROTO ((struct log_data *, struct revlist *, + RCSNode *, RCSVers *)); +static int log_symbol PROTO ((Node *, void *)); +static int log_count PROTO ((Node *, void *)); +static int log_fix_singledate PROTO ((Node *, void *)); +static int log_count_print PROTO ((Node *, void *)); +static void log_tree PROTO ((struct log_data *, struct revlist *, + RCSNode *, const char *)); +static void log_abranch PROTO ((struct log_data *, struct revlist *, + RCSNode *, const char *)); +static void log_version PROTO ((struct log_data *, struct revlist *, + RCSNode *, RCSVers *, int)); +static int log_branch PROTO ((Node *, void *)); +static int version_compare PROTO ((const char *, const char *, int)); static const char *const log_usage[] = { - "Usage: %s %s [-l] [rlog-options] [files...]\n", + "Usage: %s %s [-lRhtNb] [-r[revisions]] [-d dates] [-s states]\n", + " [-w[logins]] [files...]\n", "\t-l\tLocal directory only, no recursion.\n", + "\t-R\tOnly print name of RCS file.\n", + "\t-h\tOnly print header.\n", + "\t-t\tOnly print header and descriptive text.\n", + "\t-N\tDo not list tags.\n", + "\t-b\tOnly list revisions on the default branch.\n", + "\t-r[revisions]\tSpecify revision(s)s to list.\n", + "\t-d dates\tSpecify dates (D1<D2 for range, D for latest before).\n", + "\t-s states\tOnly list revisions with specified states.\n", + "\t-w[logins]\tOnly list revisions checked in by specified logins.\n", NULL }; -static int ac; -static char **av; - int cvslog (argc, argv) int argc; char **argv; { - int i; + int c; int err = 0; int local = 0; + struct log_data log_data; + struct option_revlist *rl, **prl; if (argc == -1) usage (log_usage); - /* - * All 'log' command options except -l are passed directly on to 'rlog' - */ - for (i = 1; i < argc && argv[i][0] == '-'; i++) - if (argv[i][1] == 'l') - local = 1; + memset (&log_data, 0, sizeof log_data); + + optind = 1; + while ((c = getopt (argc, argv, "+bd:hlNRr::s:tw::")) != -1) + { + switch (c) + { + case 'b': + log_data.default_branch = 1; + break; + case 'd': + log_parse_date (&log_data, optarg); + break; + case 'h': + log_data.header = 1; + break; + case 'l': + local = 1; + break; + case 'N': + log_data.notags = 1; + break; + case 'R': + log_data.nameonly = 1; + break; + case 'r': + rl = log_parse_revlist (optarg); + for (prl = &log_data.revlist; + *prl != NULL; + prl = &(*prl)->next) + ; + *prl = rl; + break; + case 's': + log_parse_list (&log_data.statelist, optarg); + break; + case 't': + log_data.long_header = 1; + break; + case 'w': + if (optarg != NULL) + log_parse_list (&log_data.authorlist, optarg); + else + log_parse_list (&log_data.authorlist, getcaller ()); + break; + case '?': + default: + usage (log_usage); + break; + } + } wrap_setup (); #ifdef CLIENT_SUPPORT - if (client_active) { + if (client_active) + { + int i; + /* We're the local client. Fire up the remote server. */ start_server (); @@ -62,39 +233,256 @@ cvslog (argc, argv) send_arg (argv[i]); send_file_names (argc - i, argv + i, SEND_EXPAND_WILD); -/* FIXME: We shouldn't have to send current files to get log entries, but it - doesn't work yet and I haven't debugged it. So send the files -- - it's slower but it works. gnu@cygnus.com Apr94 */ - send_files (argc - i, argv + i, local, 0); + send_files (argc - i, argv + i, local, 0, SEND_NO_CONTENTS); send_to_server ("log\012", 0); err = get_responses_and_close (); return err; } - - ac = argc; - av = argv; #endif err = start_recursion (log_fileproc, (FILESDONEPROC) NULL, log_dirproc, - (DIRLEAVEPROC) NULL, argc - i, argv + i, local, + (DIRLEAVEPROC) NULL, (void *) &log_data, + argc - optind, argv + optind, local, W_LOCAL | W_REPOS | W_ATTIC, 0, 1, - (char *) NULL, 1, 0); + (char *) NULL, 1); return (err); } +/* + * Parse a revision list specification. + */ + +static struct option_revlist * +log_parse_revlist (argstring) + const char *argstring; +{ + char *copy; + struct option_revlist *ret, **pr; + + /* Unfortunately, rlog accepts -r without an argument to mean that + latest revision on the default branch, so we must support that + for compatibility. */ + if (argstring == NULL) + { + ret = (struct option_revlist *) xmalloc (sizeof *ret); + ret->first = NULL; + ret->last = NULL; + ret->next = NULL; + ret->branchhead = 0; + return ret; + } + + ret = NULL; + pr = &ret; + + /* Copy the argument into memory so that we can change it. We + don't want to change the argument because, at least as of this + writing, we will use it if we send the arguments to the server. + We never bother to free up our copy. */ + copy = xstrdup (argstring); + while (copy != NULL) + { + char *comma; + char *cp; + char *first, *last; + struct option_revlist *r; + + comma = strchr (copy, ','); + if (comma != NULL) + *comma++ = '\0'; + + first = copy; + cp = strchr (copy, ':'); + if (cp == NULL) + last = copy; + else + { + *cp++ = '\0'; + last = cp; + } + + if (*first == '\0') + first = NULL; + if (*last == '\0') + last = NULL; + + r = (struct option_revlist *) xmalloc (sizeof *r); + r->next = NULL; + r->first = first; + r->last = last; + if (first != last + || first[strlen (first) - 1] != '.') + { + r->branchhead = 0; + } + else + { + r->branchhead = 1; + first[strlen (first) - 1] = '\0'; + } + + *pr = r; + pr = &r->next; + + copy = comma; + } + + return ret; +} + +/* + * Parse a date specification. + */ +static void +log_parse_date (log_data, argstring) + struct log_data *log_data; + const char *argstring; +{ + char *orig_copy, *copy; + + /* Copy the argument into memory so that we can change it. We + don't want to change the argument because, at least as of this + writing, we will use it if we send the arguments to the server. */ + copy = xstrdup (argstring); + orig_copy = copy; + while (copy != NULL) + { + struct datelist *nd, **pd; + char *cpend, *cp, *ds, *de; + + nd = (struct datelist *) xmalloc (sizeof *nd); + + cpend = strchr (copy, ';'); + if (cpend != NULL) + *cpend++ = '\0'; + + pd = &log_data->datelist; + nd->inclusive = 0; + + if ((cp = strchr (copy, '>')) != NULL) + { + *cp++ = '\0'; + if (*cp == '=') + { + ++cp; + nd->inclusive = 1; + } + ds = cp; + de = copy; + } + else if ((cp = strchr (copy, '<')) != NULL) + { + *cp++ = '\0'; + if (*cp == '=') + { + ++cp; + nd->inclusive = 1; + } + ds = copy; + de = cp; + } + else + { + ds = NULL; + de = copy; + pd = &log_data->singledatelist; + } + + if (ds == NULL) + nd->start = NULL; + else if (*ds != '\0') + nd->start = Make_Date (ds); + else + { + /* 1970 was the beginning of time, as far as get_date and + Make_Date are concerned. FIXME: That is true only if time_t + is a POSIX-style time and there is nothing in ANSI that + mandates that. It would be cleaner to set a flag saying + whether or not there is a start date. */ + nd->start = Make_Date ("1/1/1970 UTC"); + } + + if (*de != '\0') + nd->end = Make_Date (de); + else + { + /* We want to set the end date to some time sufficiently far + in the future to pick up all revisions that have been + created since the specified date and the time `cvs log' + completes. FIXME: The date in question only makes sense + if time_t is a POSIX-style time and it is 32 bits + and signed. We should instead be setting a flag saying + whether or not there is an end date. Note that using + something like "next week" would break the testsuite (and, + perhaps less importantly, loses if the clock is set grossly + wrong). */ + nd->end = Make_Date ("2038-01-01"); + } + + nd->next = *pd; + *pd = nd; + + copy = cpend; + } + + free (orig_copy); +} + +/* + * Parse a comma separated list of items, and add each one to *PLIST. + */ +static void +log_parse_list (plist, argstring) + List **plist; + const char *argstring; +{ + while (1) + { + Node *p; + char *cp; + + p = getnode (); + + cp = strchr (argstring, ','); + if (cp == NULL) + p->key = xstrdup (argstring); + else + { + size_t len; + + len = cp - argstring; + p->key = xmalloc (len + 1); + strncpy (p->key, argstring, len); + p->key[len + 1] = '\0'; + } + + if (*plist == NULL) + *plist = getlist (); + if (addnode (*plist, p) != 0) + freenode (p); + + if (cp == NULL) + break; + + argstring = cp + 1; + } +} /* * Do an rlog on a file */ -/* ARGSUSED */ static int -log_fileproc (finfo) +log_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { + struct log_data *log_data = (struct log_data *) callerdat; Node *p; RCSNode *rcsfile; - int retcode = 0; + char buf[50]; + struct revlist *revlist; + struct log_data_and_rcs log_data_and_rcs; if ((rcsfile = finfo->rcs) == NULL) { @@ -120,28 +508,875 @@ log_fileproc (finfo) return (1); } - run_setup ("%s%s -x,v/", Rcsbin, RCS_RLOG); + if (log_data->nameonly) + { + cvs_output (rcsfile->path, 0); + cvs_output ("\n", 1); + return 0; + } + + /* We will need all the information in the RCS file. */ + RCS_fully_parse (rcsfile); + + /* Turn any symbolic revisions in the revision list into numeric + revisions. */ + revlist = log_expand_revlist (rcsfile, log_data->revlist, + log_data->default_branch); + + /* The output here is intended to be exactly compatible with the + output of rlog. I'm not sure whether this code should be here + or in rcs.c; I put it here because it is specific to the log + function, even though it uses information gathered by the + functions in rcs.c. */ + + cvs_output ("\n", 1); + + cvs_output ("RCS file: ", 0); + cvs_output (rcsfile->path, 0); + + cvs_output ("\nWorking file: ", 0); + if (finfo->update_dir[0] == '\0') + cvs_output (finfo->file, 0); + else + { + cvs_output (finfo->update_dir, 0); + cvs_output ("/", 0); + cvs_output (finfo->file, 0); + + } + + cvs_output ("\nhead:", 0); + if (rcsfile->head != NULL) + { + cvs_output (" ", 1); + cvs_output (rcsfile->head, 0); + } + + cvs_output ("\nbranch:", 0); + if (rcsfile->branch != NULL) + { + cvs_output (" ", 1); + cvs_output (rcsfile->branch, 0); + } + + cvs_output ("\nlocks:", 0); + if (rcsfile->other != NULL) + { + p = findnode (rcsfile->other, "strict"); + if (p != NULL) + cvs_output (" strict", 0); + p = findnode (rcsfile->other, "locks"); + if (p != NULL && p->data != NULL) + { + char *f, *cp; + + f = xstrdup (p->data); + cp = f; + while (*cp != '\0') + { + char *cp2, *locker, *version; + RCSVers *vnode; + + locker = cp; + + cp2 = strchr (cp, ':'); + if (cp2 == NULL) + { + error (0, 0, "warning: bad locks field in RCS file `%s'", + finfo->fullname); + break; + } + + *cp2 = '\0'; + + cvs_output ("\n\t", 2); + cvs_output (cp, cp2 - cp); + cvs_output (": ", 2); + + cp = cp2 + 1; + while (isspace (*cp) && *cp != '\0') + ++cp; + + version = cp; + + cp2 = cp; + while (! isspace (*cp2) && *cp2 != '\0') + ++cp2; + + cvs_output (cp, cp2 - cp); + + if (*cp2 == '\0') + cp = cp2; + else + { + *cp2 = '\0'; + cp = cp2 + 1; + while (isspace (*cp) && *cp != '\0') + ++cp; + } + + p = findnode (rcsfile->versions, version); + if (p == NULL) + error (0, 0, + "warning: lock for missing version `%s' in `%s'", + version, finfo->fullname); + else + { + vnode = (RCSVers *) p->data; + p = getnode (); + p->type = RCSFIELD; + p->key = xstrdup (";locker"); + p->data = xstrdup (locker); + if (addnode (vnode->other, p) != 0) + { + error (0, 0, + "warning: duplicate lock for `%s' in `%s'", + version, finfo->fullname); + freenode (p); + } + } + } + + free (f); + } + } + + cvs_output ("\naccess list:", 0); + if (rcsfile->other != NULL) + { + p = findnode (rcsfile->other, "access"); + if (p != NULL && p->data != NULL) + { + const char *cp; + + cp = p->data; + while (*cp != '\0') + { + const char *cp2; + + cvs_output ("\n\t", 2); + cp2 = cp; + while (! isspace (*cp2) && *cp2 != '\0') + ++cp2; + cvs_output (cp, cp2 - cp); + cp = cp2; + while (isspace (*cp) && *cp != '\0') + ++cp; + } + } + } + + if (! log_data->notags) + { + List *syms; + + cvs_output ("\nsymbolic names:", 0); + syms = RCS_symbols (rcsfile); + walklist (syms, log_symbol, NULL); + } + + cvs_output ("\nkeyword substitution: ", 0); + if (rcsfile->expand == NULL) + cvs_output ("kv", 2); + else + cvs_output (rcsfile->expand, 0); + + cvs_output ("\ntotal revisions: ", 0); + sprintf (buf, "%d", walklist (rcsfile->versions, log_count, NULL)); + cvs_output (buf, 0); + + if (! log_data->header && ! log_data->long_header) + { + cvs_output (";\tselected revisions: ", 0); + + log_data_and_rcs.log_data = log_data; + log_data_and_rcs.revlist = revlist; + log_data_and_rcs.rcs = rcsfile; + + /* If any single dates were specified, we need to identify the + revisions they select. Each one selects the single + revision, which is otherwise selected, of that date or + earlier. The log_fix_singledate routine will fill in the + start date for each specific revision. */ + if (log_data->singledatelist != NULL) + walklist (rcsfile->versions, log_fix_singledate, + (void *) &log_data_and_rcs); + + sprintf (buf, "%d", walklist (rcsfile->versions, log_count_print, + (void *) &log_data_and_rcs)); + cvs_output (buf, 0); + } + + cvs_output ("\n", 1); + + if (! log_data->header || log_data->long_header) + { + cvs_output ("description:\n", 0); + if (rcsfile->other != NULL) + { + p = findnode (rcsfile->other, "desc"); + if (p != NULL && p->data != NULL) + cvs_output (p->data, 0); + } + } + + if (! log_data->header && ! log_data->long_header && rcsfile->head != NULL) + { + p = findnode (rcsfile->versions, rcsfile->head); + if (p == NULL) + error (1, 0, "can not find head revision in `%s'", + finfo->fullname); + while (p != NULL) + { + RCSVers *vers; + + vers = (RCSVers *) p->data; + log_version (log_data, revlist, rcsfile, vers, 1); + if (vers->next == NULL) + p = NULL; + else + { + p = findnode (rcsfile->versions, vers->next); + if (p == NULL) + error (1, 0, "can not find next revision `%s' in `%s'", + vers->next, finfo->fullname); + } + } + + log_tree (log_data, revlist, rcsfile, rcsfile->head); + } + + cvs_output("\ +=============================================================================\n", + 0); + + /* Free up the new revlist and restore the old one. */ + log_free_revlist (revlist); + + /* If singledatelist is not NULL, free up the start dates we added + to it. */ + if (log_data->singledatelist != NULL) + { + struct datelist *d; + + for (d = log_data->singledatelist; d != NULL; d = d->next) + { + if (d->start != NULL) + free (d->start); + d->start = NULL; + } + } + + return 0; +} + +/* + * Fix up a revision list in order to compare it against versions. + * Expand any symbolic revisions. + */ +static struct revlist * +log_expand_revlist (rcs, revlist, default_branch) + RCSNode *rcs; + struct option_revlist *revlist; + int default_branch; +{ + struct option_revlist *r; + struct revlist *ret, **pr; + + ret = NULL; + pr = &ret; + for (r = revlist; r != NULL; r = r->next) + { + struct revlist *nr; + + nr = (struct revlist *) xmalloc (sizeof *nr); + + if (r->first == NULL && r->last == NULL) + { + /* If both first and last are NULL, it means that we want + just the head of the default branch, which is RCS_head. */ + nr->first = RCS_head (rcs); + nr->last = xstrdup (nr->first); + nr->fields = numdots (nr->first) + 1; + } + else if (r->branchhead) + { + char *branch; + + /* Print just the head of the branch. */ + if (isdigit (r->first[0])) + nr->first = RCS_getbranch (rcs, r->first, 1); + else + { + branch = RCS_whatbranch (rcs, r->first); + if (branch == NULL) + { + error (0, 0, "warning: `%s' is not a branch in `%s'", + r->first, rcs->path); + free (nr); + continue; + } + nr->first = RCS_getbranch (rcs, branch, 1); + free (branch); + } + if (nr->first == NULL) + { + error (0, 0, "warning: no revision `%s' in `%s'", + r->first, rcs->path); + free (nr); + continue; + } + nr->last = xstrdup (nr->first); + nr->fields = numdots (nr->first) + 1; + } + else + { + if (r->first == NULL || isdigit (r->first[0])) + nr->first = xstrdup (r->first); + else + { + if (RCS_nodeisbranch (rcs, r->first)) + nr->first = RCS_whatbranch (rcs, r->first); + else + nr->first = RCS_gettag (rcs, r->first, 1, (int *) NULL); + if (nr->first == NULL) + { + error (0, 0, "warning: no revision `%s' in `%s'", + r->first, rcs->path); + free (nr); + continue; + } + } + + if (r->last == r->first) + nr->last = xstrdup (nr->first); + else if (r->last == NULL || isdigit (r->last[0])) + nr->last = xstrdup (r->last); + else + { + if (RCS_nodeisbranch (rcs, r->last)) + nr->last = RCS_whatbranch (rcs, r->last); + else + nr->last = RCS_gettag (rcs, r->last, 1, (int *) NULL); + if (nr->last == NULL) + { + error (0, 0, "warning: no revision `%s' in `%s'", + r->last, rcs->path); + if (nr->first != NULL) + free (nr->first); + free (nr); + continue; + } + } + + /* Process the revision numbers the same way that rlog + does. This code is a bit cryptic for my tastes, but + keeping the same implementation as rlog ensures a + certain degree of compatibility. */ + if (r->first == NULL) + { + nr->fields = numdots (nr->last) + 1; + if (nr->fields < 2) + nr->first = xstrdup (".0"); + else + { + char *cp; + + nr->first = xstrdup (nr->last); + cp = strrchr (nr->first, '.'); + strcpy (cp, ".0"); + } + } + else if (r->last == NULL) + { + nr->fields = numdots (nr->first) + 1; + nr->last = xstrdup (nr->first); + if (nr->fields < 2) + nr->last[0] = '\0'; + else + { + char *cp; + + cp = strrchr (nr->last, '.'); + *cp = '\0'; + } + } + else + { + nr->fields = numdots (nr->first) + 1; + if (nr->fields != numdots (nr->last) + 1 + || (nr->fields > 2 + && version_compare (nr->first, nr->last, + nr->fields - 1) != 0)) + { + error (0, 0, + "invalid branch or revision pair %s:%s in `%s'", + r->first, r->last, rcs->path); + free (nr->first); + free (nr->last); + free (nr); + continue; + } + if (version_compare (nr->first, nr->last, nr->fields) > 0) + { + char *tmp; + + tmp = nr->first; + nr->first = nr->last; + nr->last = tmp; + } + } + } + + nr->next = NULL; + *pr = nr; + pr = &nr->next; + } + + /* If the default branch was requested, add a revlist entry for + it. This is how rlog handles this option. */ + if (default_branch + && (rcs->head != NULL || rcs->branch != NULL)) + { + struct revlist *nr; + + nr = (struct revlist *) xmalloc (sizeof *nr); + if (rcs->branch != NULL) + nr->first = xstrdup (rcs->branch); + else + { + char *cp; + + nr->first = xstrdup (rcs->head); + cp = strrchr (nr->first, '.'); + *cp = '\0'; + } + nr->last = xstrdup (nr->first); + nr->fields = numdots (nr->first) + 1; + + nr->next = NULL; + *pr = nr; + } + + return ret; +} + +/* + * Free a revlist created by log_expand_revlist. + */ +static void +log_free_revlist (revlist) + struct revlist *revlist; +{ + struct revlist *r; + + r = revlist; + while (r != NULL) + { + struct revlist *next; + + if (r->first != NULL) + free (r->first); + if (r->last != NULL) + free (r->last); + next = r->next; + free (r); + r = next; + } +} + +/* + * Return nonzero if a revision should be printed, based on the + * options provided. + */ +static int +log_version_requested (log_data, revlist, rcs, vnode) + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; + RCSVers *vnode; +{ + /* Handle the list of states from the -s option. */ + if (log_data->statelist != NULL + && findnode (log_data->statelist, vnode->state) == NULL) + { + return 0; + } + + /* Handle the list of authors from the -w option. */ + if (log_data->authorlist != NULL) + { + if (vnode->author != NULL + && findnode (log_data->authorlist, vnode->author) == NULL) + { + return 0; + } + } + + /* rlog considers all the -d options together when it decides + whether to print a revision, so we must be compatible. */ + if (log_data->datelist != NULL || log_data->singledatelist != NULL) + { + struct datelist *d; + + for (d = log_data->datelist; d != NULL; d = d->next) + { + int cmp; + + cmp = RCS_datecmp (vnode->date, d->start); + if (cmp > 0 || (cmp == 0 && d->inclusive)) + { + cmp = RCS_datecmp (vnode->date, d->end); + if (cmp < 0 || (cmp == 0 && d->inclusive)) + break; + } + } + + if (d == NULL) + { + /* Look through the list of specific dates. We want to + select the revision with the exact date found in the + start field. The commit code ensures that it is + impossible to check in multiple revisions of a single + file in a single second, so checking the date this way + should never select more than one revision. */ + for (d = log_data->singledatelist; d != NULL; d = d->next) + { + if (d->start != NULL + && RCS_datecmp (vnode->date, d->start) == 0) + { + break; + } + } + + if (d == NULL) + return 0; + } + } + + /* If the -r or -b options were used, REVLIST will be non NULL, + and we print the union of the specified revisions. */ + if (revlist != NULL) + { + char *v; + int vfields; + struct revlist *r; + + /* This code is taken from rlog. */ + v = vnode->version; + vfields = numdots (v) + 1; + for (r = revlist; r != NULL; r = r->next) + { + if (vfields == r->fields + (r->fields & 1) + && version_compare (v, r->first, r->fields) >= 0 + && version_compare (v, r->last, r->fields) <= 0) + { + return 1; + } + } + + /* If we get here, then the -b and/or the -r option was used, + but did not match this revision, so we reject it. */ + + return 0; + } + + /* By default, we print all revisions. */ + return 1; +} + +/* + * Output a single symbol. This is called via walklist. + */ +/*ARGSUSED*/ +static int +log_symbol (p, closure) + Node *p; + void *closure; +{ + cvs_output ("\n\t", 2); + cvs_output (p->key, 0); + cvs_output (": ", 2); + cvs_output (p->data, 0); + return 0; +} + +/* + * Count the number of entries on a list. This is called via walklist. + */ +/*ARGSUSED*/ +static int +log_count (p, closure) + Node *p; + void *closure; +{ + return 1; +} + +/* + * Sort out a single date specification by narrowing down the date + * until we find the specific selected revision. + */ +static int +log_fix_singledate (p, closure) + Node *p; + void *closure; +{ + struct log_data_and_rcs *data = (struct log_data_and_rcs *) closure; + Node *pv; + RCSVers *vnode; + struct datelist *holdsingle, *holddate; + int requested; + + pv = findnode (data->rcs->versions, p->key); + if (pv == NULL) + error (1, 0, "missing version `%s' in RCS file `%s'", + p->key, data->rcs->path); + vnode = (RCSVers *) pv->data; + + /* We are only interested if this revision passes any other tests. + Temporarily clear log_data->singledatelist to avoid confusing + log_version_requested. We also clear log_data->datelist, + because rlog considers all the -d options together. We don't + want to reject a revision because it does not match a date pair + if we are going to select it on the basis of the singledate. */ + holdsingle = data->log_data->singledatelist; + data->log_data->singledatelist = NULL; + holddate = data->log_data->datelist; + data->log_data->datelist = NULL; + requested = log_version_requested (data->log_data, data->revlist, + data->rcs, vnode); + data->log_data->singledatelist = holdsingle; + data->log_data->datelist = holddate; + + if (requested) + { + struct datelist *d; + + /* For each single date, if this revision is before the + specified date, but is closer than the previously selected + revision, select it instead. */ + for (d = data->log_data->singledatelist; d != NULL; d = d->next) + { + if (RCS_datecmp (vnode->date, d->end) <= 0 + && (d->start == NULL + || RCS_datecmp (vnode->date, d->start) > 0)) + { + if (d->start != NULL) + free (d->start); + d->start = xstrdup (vnode->date); + } + } + } + + return 0; +} + +/* + * Count the number of revisions we are going to print. + */ +static int +log_count_print (p, closure) + Node *p; + void *closure; +{ + struct log_data_and_rcs *data = (struct log_data_and_rcs *) closure; + Node *pv; + + pv = findnode (data->rcs->versions, p->key); + if (pv == NULL) + error (1, 0, "missing version `%s' in RCS file `%s'", + p->key, data->rcs->path); + if (log_version_requested (data->log_data, data->revlist, data->rcs, + (RCSVers *) pv->data)) + return 1; + else + return 0; +} + +/* + * Print the list of changes, not including the trunk, in reverse + * order for each branch. + */ +static void +log_tree (log_data, revlist, rcs, ver) + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; + const char *ver; +{ + Node *p; + RCSVers *vnode; + + p = findnode (rcs->versions, ver); + if (p == NULL) + error (1, 0, "missing version `%s' in RCS file `%s'", + ver, rcs->path); + vnode = (RCSVers *) p->data; + if (vnode->next != NULL) + log_tree (log_data, revlist, rcs, vnode->next); + if (vnode->branches != NULL) + { + Node *head, *branch; + + /* We need to do the branches in reverse order. This breaks + the List abstraction, but so does most of the branch + manipulation in rcs.c. */ + head = vnode->branches->list; + for (branch = head->prev; branch != head; branch = branch->prev) + { + log_abranch (log_data, revlist, rcs, branch->key); + log_tree (log_data, revlist, rcs, branch->key); + } + } +} + +/* + * Log the changes for a branch, in reverse order. + */ +static void +log_abranch (log_data, revlist, rcs, ver) + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; + const char *ver; +{ + Node *p; + RCSVers *vnode; + + p = findnode (rcs->versions, ver); + if (p == NULL) + error (1, 0, "missing version `%s' in RCS file `%s'", + ver, rcs->path); + vnode = (RCSVers *) p->data; + if (vnode->next != NULL) + log_abranch (log_data, revlist, rcs, vnode->next); + log_version (log_data, revlist, rcs, vnode, 0); +} + +/* + * Print the log output for a single version. + */ +static void +log_version (log_data, revlist, rcs, ver, trunk) + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; + RCSVers *ver; + int trunk; +{ + Node *p; + int year, mon, mday, hour, min, sec; + char buf[100]; + Node *padd, *pdel; + + if (! log_version_requested (log_data, revlist, rcs, ver)) + return; + + cvs_output ("----------------------------\nrevision ", 0); + cvs_output (ver->version, 0); + + p = findnode (ver->other, ";locker"); + if (p != NULL) + { + cvs_output ("\tlocked by: ", 0); + cvs_output (p->data, 0); + cvs_output (";", 1); + } + + cvs_output ("\ndate: ", 0); + (void) sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min, + &sec); + if (year < 1900) + year += 1900; + sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday, + hour, min, sec); + cvs_output (buf, 0); + + cvs_output ("; author: ", 0); + cvs_output (ver->author, 0); + + cvs_output ("; state: ", 0); + cvs_output (ver->state, 0); + cvs_output (";", 1); + + if (! trunk) + { + padd = findnode (ver->other, ";add"); + pdel = findnode (ver->other, ";delete"); + } + else if (ver->next == NULL) + { + padd = NULL; + pdel = NULL; + } + else + { + Node *nextp; + RCSVers *nextver; + + nextp = findnode (rcs->versions, ver->next); + if (nextp == NULL) + error (1, 0, "missing version `%s' in `%s'", ver->next, + rcs->path); + nextver = (RCSVers *) nextp->data; + pdel = findnode (nextver->other, ";add"); + padd = findnode (nextver->other, ";delete"); + } + + if (padd != NULL) + { + cvs_output (" lines: +", 0); + cvs_output (padd->data, 0); + cvs_output (" -", 2); + cvs_output (pdel->data, 0); + } + + if (ver->branches != NULL) { - int i; - for (i = 1; i < ac && av[i][0] == '-'; i++) - if (av[i][1] != 'l') - run_arg (av[i]); + cvs_output ("\nbranches:", 0); + walklist (ver->branches, log_branch, (void *) NULL); } - run_arg (rcsfile->path); - if (*finfo->update_dir) + cvs_output ("\n", 1); + + p = findnode (ver->other, "log"); + if (p == NULL || p->data[0] == '\0') + cvs_output ("*** empty log message ***\n", 0); + else { - char *workfile = xmalloc (strlen (finfo->update_dir) + strlen (finfo->file) + 2); - sprintf (workfile, "%s/%s", finfo->update_dir, finfo->file); - run_arg (workfile); - free (workfile); + /* FIXME: Technically, the log message could contain a null + byte. */ + cvs_output (p->data, 0); + if (p->data[strlen (p->data) - 1] != '\n') + cvs_output ("\n", 1); } +} - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_REALLY)) == -1) +/* + * Output a branch version. This is called via walklist. + */ +/*ARGSUSED*/ +static int +log_branch (p, closure) + Node *p; + void *closure; +{ + cvs_output (" ", 2); + if ((numdots (p->key) & 1) == 0) + cvs_output (p->key, 0); + else { - error (1, errno, "fork failed for rlog on %s", finfo->file); + char *f, *cp; + + f = xstrdup (p->key); + cp = strrchr (f, '.'); + *cp = '\0'; + cvs_output (f, 0); + free (f); } - return (retcode); + cvs_output (";", 1); + return 0; } /* @@ -149,10 +1384,12 @@ log_fileproc (finfo) */ /* ARGSUSED */ static Dtype -log_dirproc (dir, repository, update_dir) +log_dirproc (callerdat, dir, repository, update_dir, entries) + void *callerdat; char *dir; char *repository; char *update_dir; + List *entries; { if (!isdir (dir)) return (R_SKIP_ALL); @@ -161,3 +1398,52 @@ log_dirproc (dir, repository, update_dir) error (0, 0, "Logging %s", update_dir); return (R_PROCESS); } + +/* + * Compare versions. This is taken from RCS compartial. + */ +static int +version_compare (v1, v2, len) + const char *v1; + const char *v2; + int len; +{ + while (1) + { + int d1, d2, r; + + if (*v1 == '\0') + return 1; + if (*v2 == '\0') + return -1; + + while (*v1 == '0') + ++v1; + for (d1 = 0; isdigit (v1[d1]); ++d1) + ; + + while (*v2 == '0') + ++v2; + for (d2 = 0; isdigit (v2[d2]); ++d2) + ; + + if (d1 != d2) + return d1 < d2 ? -1 : 1; + + r = memcmp (v1, v2, d1); + if (r != 0) + return r; + + --len; + if (len == 0) + return 0; + + v1 += d1; + v2 += d1; + + if (*v1 == '.') + ++v1; + if (*v2 == '.') + ++v2; + } +} diff --git a/contrib/cvs/src/login.c b/contrib/cvs/src/login.c index fc3a178..cc277a6 100644 --- a/contrib/cvs/src/login.c +++ b/contrib/cvs/src/login.c @@ -12,7 +12,16 @@ #ifdef AUTH_CLIENT_SUPPORT /* This covers the rest of the file. */ +/* There seems to be very little agreement on which system header + getpass is declared in. With a lot of fancy autoconfiscation, + we could perhaps detect this, but for now we'll just rely on + _CRAY, since Cray is perhaps the only system on which our own + declaration won't work (some Crays declare the 2#$@% thing as + varadic, believe it or not). On Cray, getpass will be declared + in either stdlib.h or unistd.h. */ +#ifndef _CRAY extern char *getpass (); +#endif #ifndef CVS_PASSWORD_FILE #define CVS_PASSWORD_FILE ".cvspass" @@ -21,40 +30,53 @@ extern char *getpass (); /* If non-NULL, get_cvs_password() will just return this. */ static char *cvs_password = NULL; +static char *construct_cvspass_filename PROTO ((void)); + /* The return value will need to be freed. */ -char * +static char * construct_cvspass_filename () { - char *homedir; - char *passfile; + char *homedir; + char *passfile; - /* Environment should override file. */ - if ((passfile = getenv ("CVS_PASSFILE")) != NULL) - return xstrdup (passfile); + /* Environment should override file. */ + if ((passfile = getenv ("CVS_PASSFILE")) != NULL) + return xstrdup (passfile); - /* Construct absolute pathname to user's password file. */ - /* todo: does this work under OS/2 ? */ - homedir = get_homedir (); - if (! homedir) + /* Construct absolute pathname to user's password file. */ + /* todo: does this work under OS/2 ? */ + homedir = get_homedir (); + if (! homedir) { - error (1, errno, "could not find out home directory"); - return (char *) NULL; + error (1, errno, "could not find out home directory"); + return (char *) NULL; } - - passfile = - (char *) xmalloc (strlen (homedir) + strlen (CVS_PASSWORD_FILE) + 3); - strcpy (passfile, homedir); - strcat (passfile, "/"); - strcat (passfile, CVS_PASSWORD_FILE); - - /* Safety first and last, Scouts. */ - if (isfile (passfile)) - /* xchmod() is too polite. */ - chmod (passfile, 0600); - return passfile; + passfile = + (char *) xmalloc (strlen (homedir) + strlen (CVS_PASSWORD_FILE) + 3); + strcpy (passfile, homedir); +#ifndef NO_SLASH_AFTER_HOME + /* NO_SLASH_AFTER_HOME is defined for VMS, where foo:[bar]/.cvspass is not + a legal filename but foo:[bar].cvspass is. A more clean solution would + be something more along the lines of a "join a directory to a filename" + kind of thing.... */ + strcat (passfile, "/"); +#endif + strcat (passfile, CVS_PASSWORD_FILE); + + /* Safety first and last, Scouts. */ + if (isfile (passfile)) + /* xchmod() is too polite. */ + chmod (passfile, 0600); + + return passfile; } +static const char *const login_usage[] = +{ + "Usage: %s %s\n", + NULL +}; /* Prompt for a password, and store it in the file "CVS/.cvspass". * @@ -84,309 +106,354 @@ login (argc, argv) int argc; char **argv; { - char *passfile; - FILE *fp; - char *typed_password, *found_password; - char *linebuf = (char *) NULL; - size_t linebuf_len; - int root_len, already_entered = 0; - - /* Make this a "fully-qualified" CVSroot if necessary. */ - if (! strchr (CVSroot, '@')) - { - /* We need to prepend "user@host:". */ - char *tmp; + char *passfile; + FILE *fp; + char *typed_password, *found_password; + char *linebuf = (char *) NULL; + size_t linebuf_len; + int root_len, already_entered = 0; - printf ("Repository \"%s\" not fully-qualified.\n", CVSroot); - printf ("Please enter \"user@host:/path\": "); - fflush (stdout); - getline (&linebuf, &linebuf_len, stdin); + if (argc < 0) + usage (login_usage); - tmp = xmalloc (strlen (linebuf) + 1); - - /* Give it some permanent storage. */ - strcpy (tmp, linebuf); - tmp[strlen (linebuf) - 1] = '\0'; - CVSroot = tmp; - - /* Reset. */ - free (linebuf); - linebuf = (char *) NULL; + if (CVSroot_method != pserver_method) + { + error (0, 0, "can only use pserver method with `login' command"); + error (1, 0, "CVSROOT: %s", CVSroot_original); } - - if (CVSroot[0] != ':') + + if (! CVSroot_username) { - /* Then we need to prepend ":pserver:". */ - char *tmp; - - tmp = xmalloc (strlen (":pserver:") + strlen (CVSroot) + 1); - strcpy (tmp, ":pserver:"); - strcat (tmp, CVSroot); - CVSroot = tmp; + error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", + CVSroot_original); + error (1, 0, "Please make sure to specify \"user@host\"!"); } - /* Check to make sure it's fully-qualified before going on. - * Fully qualified in this context means it has both a user and a - * host:repos portion. - */ - { - char *r; - - /* After confirming that CVSroot is non-NULL, we skip past the - initial ":pserver:" to test the rest of it. */ - - if (! CVSroot) - error (1, 0, "CVSroot is NULL"); - else if (! strchr ((r = (CVSroot + strlen (":pserver:"))), '@')) - goto not_fqrn; - else if (! strchr (r, ':')) - goto not_fqrn; - - if (0) /* Lovely. */ - { - not_fqrn: - error (0, 0, "CVSroot not fully-qualified: %s", CVSroot); - error (1, 0, "should be format user@host:/path/to/repository"); - } - } - - /* CVSroot is now fully qualified and has ":pserver:" prepended. - We'll print out most of it so user knows exactly what is being - dealt with here. */ - { - char *s; - s = strchr (CVSroot, ':'); - s++; - s = strchr (s, ':'); - s++; - - if (s == NULL) - error (1, 0, "NULL CVSroot"); - - printf ("(Logging in to %s)\n", s); + printf ("(Logging in to %s@%s)\n", CVSroot_username, CVSroot_hostname); fflush (stdout); - } - passfile = construct_cvspass_filename (); - typed_password = getpass ("CVS password: "); - typed_password = scramble (typed_password); + passfile = construct_cvspass_filename (); + typed_password = getpass ("CVS password: "); + typed_password = scramble (typed_password); - /* Force get_cvs_password() to use this one (when the client - * confirms the new password with the server), instead of consulting - * the file. We make a new copy because cvs_password will get - * zeroed by connect_to_server(). - */ - cvs_password = xstrdup (typed_password); + /* Force get_cvs_password() to use this one (when the client + * confirms the new password with the server), instead of + * consulting the file. We make a new copy because cvs_password + * will get zeroed by connect_to_server(). */ - if (connect_to_pserver (NULL, NULL, 1) == 0) + cvs_password = xstrdup (typed_password); + + if (connect_to_pserver (NULL, NULL, 1) == 0) { - /* The password is wrong, according to the server. */ - error (1, 0, "incorrect password"); + /* The password is wrong, according to the server. */ + error (1, 0, "incorrect password"); } - /* IF we have a password for this "[user@]host:/path" already - * THEN - * IF it's the same as the password we read from the prompt - * THEN - * do nothing - * ELSE - * replace the old password with the new one - * ELSE - * append new entry to the end of the file. - */ - - root_len = strlen (CVSroot); - - /* Yes, the method below reads the user's password file twice. It's - inefficient, but we're not talking about a gig of data here. */ - - fp = fopen (passfile, "r"); - /* FIXME: should be printing a message if fp == NULL and not - existence_error (errno). */ - if (fp != NULL) + /* IF we have a password for this "[user@]host:/path" already + * THEN + * IF it's the same as the password we read from the prompt + * THEN + * do nothing + * ELSE + * replace the old password with the new one + * ELSE + * append new entry to the end of the file. + */ + + root_len = strlen (CVSroot_original); + + /* Yes, the method below reads the user's password file twice. It's + inefficient, but we're not talking about a gig of data here. */ + + fp = CVS_FOPEN (passfile, "r"); + /* FIXME: should be printing a message if fp == NULL and not + existence_error (errno). */ + if (fp != NULL) { - /* Check each line to see if we have this entry already. */ - while (getline (&linebuf, &linebuf_len, fp) >= 0) + /* Check each line to see if we have this entry already. */ + while (getline (&linebuf, &linebuf_len, fp) >= 0) { - if (strncmp (CVSroot, linebuf, root_len) == 0) - { - already_entered = 1; - break; - } - else + if (strncmp (CVSroot_original, linebuf, root_len) == 0) { - free (linebuf); - linebuf = (char *) NULL; + already_entered = 1; + break; } } - fclose (fp); + fclose (fp); } - - if (already_entered) + + if (already_entered) { - /* This user/host has a password in the file already. */ + /* This user/host has a password in the file already. */ - strtok (linebuf, " "); - found_password = strtok (NULL, "\n"); - if (strcmp (found_password, typed_password)) + strtok (linebuf, " "); + found_password = strtok (NULL, "\n"); + if (strcmp (found_password, typed_password)) { - /* typed_password and found_password don't match, so we'll - * have to update passfile. We replace the old password - * with the new one by writing a tmp file whose contents are - * exactly the same as passfile except that this one entry - * gets typed_password instead of found_password. Then we - * rename the tmp file on top of passfile. - */ - char *tmp_name; - FILE *tmp_fp; - - tmp_name = tmpnam (NULL); - if ((tmp_fp = fopen (tmp_name, "w")) == NULL) + /* typed_password and found_password don't match, so we'll + * have to update passfile. We replace the old password + * with the new one by writing a tmp file whose contents are + * exactly the same as passfile except that this one entry + * gets typed_password instead of found_password. Then we + * rename the tmp file on top of passfile. + */ + char *tmp_name; + FILE *tmp_fp; + + tmp_name = cvs_temp_name (); + if ((tmp_fp = CVS_FOPEN (tmp_name, "w")) == NULL) { - error (1, errno, "unable to open temp file %s", tmp_name); - return 1; + error (1, errno, "unable to open temp file %s", tmp_name); + return 1; } - chmod (tmp_name, 0600); + chmod (tmp_name, 0600); - fp = fopen (passfile, "r"); - if (fp == NULL) + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) { - error (1, errno, "unable to open %s", passfile); - return 1; + error (1, errno, "unable to open %s", passfile); + if (linebuf) + free (linebuf); + return 1; } - /* I'm not paranoid, they really ARE out to get me: */ - chmod (passfile, 0600); + /* I'm not paranoid, they really ARE out to get me: */ + chmod (passfile, 0600); - free (linebuf); - linebuf = (char *) NULL; - while (getline (&linebuf, &linebuf_len, fp) >= 0) + while (getline (&linebuf, &linebuf_len, fp) >= 0) { - if (strncmp (CVSroot, linebuf, root_len)) - fprintf (tmp_fp, "%s", linebuf); - else - fprintf (tmp_fp, "%s %s\n", CVSroot, typed_password); + if (strncmp (CVSroot_original, linebuf, root_len)) + fprintf (tmp_fp, "%s", linebuf); + else + fprintf (tmp_fp, "%s %s\n", CVSroot_original, + typed_password); - free (linebuf); - linebuf = (char *) NULL; } - fclose (tmp_fp); - fclose (fp); - rename_file (tmp_name, passfile); - chmod (passfile, 0600); + if (linebuf) + free (linebuf); + fclose (tmp_fp); + fclose (fp); + copy_file (tmp_name, passfile); + unlink_file (tmp_name); + chmod (passfile, 0600); + free (tmp_name); } } - else + else { - if ((fp = fopen (passfile, "a")) == NULL) + if (linebuf) + free (linebuf); + if ((fp = CVS_FOPEN (passfile, "a")) == NULL) { - error (1, errno, "could not open %s", passfile); - free (passfile); - return 1; + error (1, errno, "could not open %s", passfile); + free (passfile); + return 1; } - fprintf (fp, "%s %s\n", CVSroot, typed_password); - fclose (fp); + fprintf (fp, "%s %s\n", CVSroot_original, typed_password); + fclose (fp); } - /* Utter, total, raving paranoia, I know. */ - chmod (passfile, 0600); - memset (typed_password, 0, strlen (typed_password)); - free (typed_password); + /* Utter, total, raving paranoia, I know. */ + chmod (passfile, 0600); + memset (typed_password, 0, strlen (typed_password)); + free (typed_password); - free (passfile); - free (cvs_password); - cvs_password = NULL; - return 0; + free (passfile); + free (cvs_password); + cvs_password = NULL; + return 0; } -/* todo: "cvs logout" could erase an entry from the file. - * But to what purpose? - */ - /* Returns the _scrambled_ password. The server must descramble before hashing and comparing. */ char * get_cvs_password () { - int found_it = 0; - int root_len; - char *password; - char *linebuf = (char *) NULL; - size_t linebuf_len; - FILE *fp; - char *passfile; - - /* If someone (i.e., login()) is calling connect_to_pserver() out of - context, then assume they have supplied the correct, scrambled - password. */ - if (cvs_password) - return cvs_password; - - /* Environment should override file. */ - if ((password = getenv ("CVS_PASSWORD")) != NULL) + int found_it = 0; + int root_len; + char *password; + char *linebuf = (char *) NULL; + size_t linebuf_len; + FILE *fp; + char *passfile; + + /* If someone (i.e., login()) is calling connect_to_pserver() out of + context, then assume they have supplied the correct, scrambled + password. */ + if (cvs_password) + return cvs_password; + + if (getenv ("CVS_PASSWORD") != NULL) + { + /* In previous versions of CVS one could specify a password in + CVS_PASSWORD. This is a bad idea, because in BSD variants + of unix anyone can see the environment variable with 'ps'. + But for users who were using that feature we want to at + least let them know what is going on. After printing this + warning, we should fall through to the regular error where + we tell them to run "cvs login" (unless they already ran + it, of course). */ + error (0, 0, "CVS_PASSWORD is no longer supported; ignored"); + } + + /* Else get it from the file. First make sure that the CVSROOT + variable has the appropriate fields filled in. */ + + if (CVSroot_method != pserver_method) + { + error (0, 0, "can only call GET_CVS_PASSWORD with pserver method"); + error (1, 0, "CVSROOT: %s", CVSroot_original); + } + + if (! CVSroot_username) { - char *p; - p = xstrdup (password); - /* If we got it from the environment, then it wasn't properly - scrambled. Since unscrambling is done on the server side, we - need to transmit it scrambled. */ - p = scramble (p); - return p; + error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", + CVSroot_original); + error (1, 0, "Please make sure to specify \"user@host\"!"); } - /* Else get it from the file. */ - passfile = construct_cvspass_filename (); - fp = fopen (passfile, "r"); - if (fp == NULL) + passfile = construct_cvspass_filename (); + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) { - error (0, errno, "could not open %s", passfile); - free (passfile); - error (1, 0, "use \"cvs login\" to log in first"); + error (0, errno, "could not open %s", passfile); + free (passfile); + error (1, 0, "use \"cvs login\" to log in first"); } - root_len = strlen (CVSroot); + root_len = strlen (CVSroot_original); - /* Check each line to see if we have this entry already. */ - while (getline (&linebuf, &linebuf_len, fp) >= 0) + /* Check each line to see if we have this entry already. */ + while (getline (&linebuf, &linebuf_len, fp) >= 0) { - if (strncmp (CVSroot, linebuf, root_len) == 0) - { - /* This is it! So break out and deal with linebuf. */ - found_it = 1; - break; - } - else + if (strncmp (CVSroot_original, linebuf, root_len) == 0) { - free (linebuf); - linebuf = (char *) NULL; + /* This is it! So break out and deal with linebuf. */ + found_it = 1; + break; } } - if (found_it) + if (found_it) { - /* linebuf now contains the line with the password. */ - char *tmp; - - strtok (linebuf, " "); - password = strtok (NULL, "\n"); - - /* Give it permanent storage. */ - tmp = xmalloc (strlen (password) + 1); - strcpy (tmp, password); - tmp[strlen (password)] = '\0'; - memset (password, 0, strlen (password)); - free (linebuf); - return tmp; + /* linebuf now contains the line with the password. */ + char *tmp; + + strtok (linebuf, " "); + password = strtok (NULL, "\n"); + + /* Give it permanent storage. */ + tmp = xstrdup (password); + memset (password, 0, strlen (password)); + free (linebuf); + return tmp; } - else + else { - error (0, 0, "cannot find password"); - error (0, 0, "use \"cvs login\" to log in first"); - error (1, 0, "or set the CVS_PASSWORD environment variable"); + if (linebuf) + free (linebuf); + error (0, 0, "cannot find password"); + error (1, 0, "use \"cvs login\" to log in first"); } - /* NOTREACHED */ - return NULL; + /* NOTREACHED */ + return NULL; } -#endif /* AUTH_CLIENT_SUPPORT from beginning of file. */ +static const char *const logout_usage[] = +{ + "Usage: %s %s\n", + NULL +}; + +/* Remove any entry for the CVSRoot repository found in "CVS/.cvspass". */ +int +logout (argc, argv) + int argc; + char **argv; +{ + char *passfile; + FILE *fp; + char *tmp_name; + FILE *tmp_fp; + char *linebuf = (char *) NULL; + size_t linebuf_len; + int root_len, found = 0; + + if (argc < 0) + usage (logout_usage); + + if (CVSroot_method != pserver_method) + { + error (0, 0, "can only use pserver method with `logout' command"); + error (1, 0, "CVSROOT: %s", CVSroot_original); + } + + if (! CVSroot_username) + { + error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", + CVSroot_original); + error (1, 0, "Please make sure to specify \"user@host\"!"); + } + + /* Hmm. Do we want a variant of this command which deletes _all_ + the entries from the current .cvspass? Might be easier to + remember than "rm ~/.cvspass" but then again if people are + mucking with HOME (common in Win95 as the system doesn't set + it), then this variant of "cvs logout" might give a false sense + of security, in that it wouldn't delete entries from any + .cvspass files but the current one. */ + + printf ("(Logging out of %s@%s)\n", CVSroot_username, CVSroot_hostname); + fflush (stdout); + /* IF we have a password for this "[user@]host:/path" already + * THEN + * drop the entry + * ELSE + * do nothing + */ + + passfile = construct_cvspass_filename (); + tmp_name = cvs_temp_name (); + if ((tmp_fp = CVS_FOPEN (tmp_name, "w")) == NULL) + { + error (1, errno, "unable to open temp file %s", tmp_name); + return 1; + } + chmod (tmp_name, 0600); + + root_len = strlen (CVSroot_original); + + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) + error (1, errno, "Error opening %s", passfile); + + /* Check each line to see if we have this entry. */ + /* Copy only those lines that do not match this entry */ + while (getline (&linebuf, &linebuf_len, fp) >= 0) + { + if (strncmp (CVSroot_original, linebuf, root_len)) + fprintf (tmp_fp, "%s", linebuf); + else + found = TRUE; + } + if (linebuf) + free (linebuf); + fclose (fp); + fclose (tmp_fp); + + if (! found) + { + printf ("Entry not found for %s\n", CVSroot_original); + unlink_file (tmp_name); + } + else + { + copy_file (tmp_name, passfile); + unlink_file (tmp_name); + chmod (passfile, 0600); + } + return 0; +} + +#endif /* AUTH_CLIENT_SUPPORT from beginning of file. */ diff --git a/contrib/cvs/src/logmsg.c b/contrib/cvs/src/logmsg.c index 370ceab..98e4ae1 100644 --- a/contrib/cvs/src/logmsg.c +++ b/contrib/cvs/src/logmsg.c @@ -11,18 +11,20 @@ static int find_type PROTO((Node * p, void *closure)); static int fmt_proc PROTO((Node * p, void *closure)); -static int logfile_write PROTO((char *repository, char *filter, char *title, - char *message, char *revision, FILE * logfp, - List * changes)); +static int logfile_write PROTO((char *repository, char *filter, + char *message, FILE * logfp, List * changes)); static int rcsinfo_proc PROTO((char *repository, char *template)); static int title_proc PROTO((Node * p, void *closure)); static int update_logfile_proc PROTO((char *repository, char *filter)); static void setup_tmpfile PROTO((FILE * xfp, char *xprefix, List * changes)); static int editinfo_proc PROTO((char *repository, char *template)); +static int verifymsg_proc PROTO((char *repository, char *script)); static FILE *fp; static char *str_list; +static char *str_list_format; /* The format for str_list's contents. */ static char *editinfo_editor; +static char *verifymsg_script; static Ctype type; /* @@ -31,6 +33,7 @@ static Ctype type; * and removed files are included (if any) and formatted to look pretty. */ static char *prefix; static int col; +static char *tag; static void setup_tmpfile (xfp, xprefix, changes) FILE *xfp; @@ -45,28 +48,40 @@ setup_tmpfile (xfp, xprefix, changes) if (walklist (changes, find_type, NULL) != 0) { (void) fprintf (fp, "%sModified Files:\n", prefix); - (void) fprintf (fp, "%s\t", prefix); - col = 8; + col = 0; (void) walklist (changes, fmt_proc, NULL); (void) fprintf (fp, "\n"); + if (tag != NULL) + { + free (tag); + tag = NULL; + } } type = T_ADDED; if (walklist (changes, find_type, NULL) != 0) { (void) fprintf (fp, "%sAdded Files:\n", prefix); - (void) fprintf (fp, "%s\t", prefix); - col = 8; + col = 0; (void) walklist (changes, fmt_proc, NULL); (void) fprintf (fp, "\n"); + if (tag != NULL) + { + free (tag); + tag = NULL; + } } type = T_REMOVED; if (walklist (changes, find_type, NULL) != 0) { (void) fprintf (fp, "%sRemoved Files:\n", prefix); - (void) fprintf (fp, "%s\t", prefix); - col = 8; + col = 0; (void) walklist (changes, fmt_proc, NULL); (void) fprintf (fp, "\n"); + if (tag != NULL) + { + free (tag); + tag = NULL; + } } } @@ -78,7 +93,10 @@ find_type (p, closure) Node *p; void *closure; { - if (p->data == (char *) type) + struct logfile_info *li; + + li = (struct logfile_info *) p->data; + if (li->type == type) return (1); else return (0); @@ -94,9 +112,44 @@ fmt_proc (p, closure) Node *p; void *closure; { - if (p->data == (char *) type) + struct logfile_info *li; + + li = (struct logfile_info *) p->data; + if (li->type == type) { - if ((col + (int) strlen (p->key)) > 70) + if (li->tag == NULL + ? tag != NULL + : tag == NULL || strcmp (tag, li->tag) != 0) + { + if (col > 0) + (void) fprintf (fp, "\n"); + (void) fprintf (fp, "%s", prefix); + col = strlen (prefix); + while (col < 6) + { + (void) fprintf (fp, " "); + ++col; + } + + if (li->tag == NULL) + (void) fprintf (fp, "No tag"); + else + (void) fprintf (fp, "Tag: %s", li->tag); + + if (tag != NULL) + free (tag); + tag = xstrdup (li->tag); + + /* Force a new line. */ + col = 70; + } + + if (col == 0) + { + (void) fprintf (fp, "%s\t", prefix); + col = 8; + } + else if (col > 8 && (col + (int) strlen (p->key)) > 70) { (void) fprintf (fp, "\n%s\t", prefix); col = 8; @@ -126,7 +179,7 @@ do_editor (dir, messagep, repository, changes) char *line; int line_length; size_t line_chars_allocated; - char fname[L_tmpnam+1]; + char *fname; struct stat pre_stbuf, post_stbuf; int retcode = 0; char *p; @@ -138,17 +191,19 @@ do_editor (dir, messagep, repository, changes) if (strcmp (Editor, "") == 0 && !editinfo_editor) error(1, 0, "no editor defined, must use -e or -m"); + /* Create a temporary file */ - (void) tmpnam (fname); + fname = cvs_temp_name (); again: - if ((fp = fopen (fname, "w+")) == NULL) + if ((fp = CVS_FOPEN (fname, "w+")) == NULL) error (1, 0, "cannot create temporary file %s", fname); if (*messagep) { (void) fprintf (fp, "%s", *messagep); - if ((*messagep)[strlen (*messagep) - 1] != '\n') + if ((*messagep)[0] == '\0' || + (*messagep)[strlen (*messagep) - 1] != '\n') (void) fprintf (fp, "\n"); } else @@ -166,7 +221,7 @@ do_editor (dir, messagep, repository, changes) size_t nwrite; /* Why "b"? */ - tfp = fopen (CVSADM_TEMPLATE, "rb"); + tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb"); if (tfp == NULL) { if (!existence_error (errno)) @@ -197,8 +252,9 @@ do_editor (dir, messagep, repository, changes) "%s----------------------------------------------------------------------\n", CVSEDITPREFIX); (void) fprintf (fp, - "%sEnter Log. Lines beginning with `%s' are removed automatically\n%s\n", - CVSEDITPREFIX, CVSEDITPREFIX, CVSEDITPREFIX); + "%sEnter Log. Lines beginning with `%.*s' are removed automatically\n%s\n", + CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX, + CVSEDITPREFIX); if (dir != NULL && *dir) (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX, dir, CVSEDITPREFIX); @@ -211,12 +267,17 @@ do_editor (dir, messagep, repository, changes) /* finish off the temp file */ if (fclose (fp) == EOF) error (1, errno, "%s", fname); - if (stat (fname, &pre_stbuf) == -1) + if ( CVS_STAT (fname, &pre_stbuf) == -1) pre_stbuf.st_mtime = 0; if (editinfo_editor) free (editinfo_editor); editinfo_editor = (char *) NULL; +#ifdef CLIENT_SUPPORT + if (client_active) + ; /* nothing, leave editinfo_editor NULL */ + else +#endif if (repository != NULL) (void) Parse_Info (CVSROOTADM_EDITINFO, repository, editinfo_proc, 0); @@ -236,7 +297,7 @@ do_editor (dir, messagep, repository, changes) if (*messagep) free (*messagep); - if (stat (fname, &post_stbuf) != 0) + if ( CVS_STAT (fname, &post_stbuf) != 0) error (1, errno, "cannot find size of temp file %s", fname); if (post_stbuf.st_size == 0) @@ -264,7 +325,7 @@ do_editor (dir, messagep, repository, changes) error (0, errno, "warning: cannot read %s", fname); break; } - if (strncmp (line, CVSEDITPREFIX, sizeof (CVSEDITPREFIX) - 1) == 0) + if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) continue; (void) strcpy (p, line); p += line_length; @@ -288,7 +349,11 @@ do_editor (dir, messagep, repository, changes) || *line == '\n' || *line == 'c' || *line == 'C') break; if (*line == 'a' || *line == 'A') - error (1, 0, "aborted by user"); + { + if (unlink_file (fname) < 0) + error (0, errno, "warning: cannot remove temp file %s", fname); + error (1, 0, "aborted by user"); + } if (*line == 'e' || *line == 'E') goto again; if (*line == '!') @@ -303,6 +368,85 @@ do_editor (dir, messagep, repository, changes) free (line); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); + free (fname); +} + +/* Runs the user-defined verification script as part of the commit or import + process. This verification is meant to be run whether or not the user + included the -m atribute. unlike the do_editor function, this is + independant of the running of an editor for getting a message. + */ +void +do_verify (message, repository) + char *message; + char *repository; +{ + FILE *fp; + char *fname; + int retcode = 0; + +#ifdef CLIENT_SUPPORT + if (client_active) + /* The verification will happen on the server. */ + return; +#endif + + /* FIXME? Do we really want to skip this on noexec? What do we do + for the other administrative files? */ + if (noexec) + return; + + /* If there's no message, then we have nothing to verify. Can this + case happen? And if so why would we print a message? */ + if (message == NULL) + { + cvs_output ("No message to verify\n", 0); + return; + } + + /* Get a temp filename, open a temporary file, write the message to the + temp file, and close the file. */ + + fname = cvs_temp_name (); + + fp = fopen (fname, "w"); + if (fp == NULL) + { + error (1, errno, "cannot create temporary file %s", fname); + return; + } + else + { + fprintf (fp, "%s", message); + if ((message)[0] == '\0' || + (message)[strlen (message) - 1] != '\n') + (void) fprintf (fp, "%s", "\n"); + if (fclose (fp) == EOF) + error (1, errno, "%s", fname); + + /* Get the name of the verification script to run */ + + if (repository != NULL) + (void) Parse_Info (CVSROOTADM_VERIFYMSG, repository, + verifymsg_proc, 0); + + /* Run the verification script */ + + if (verifymsg_script) + { + run_setup ("%s", verifymsg_script); + run_arg (fname); + if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, + RUN_NORMAL | RUN_SIGIGNORE)) != 0) + error (1, retcode == -1 ? errno : 0, + "Message verification failed"); + } + + /* Close and delete the temp file */ + + unlink_file (fname); + free (fname); + } } /* @@ -326,7 +470,7 @@ rcsinfo_proc (repository, template) free (last_template); last_template = xstrdup (template); - if ((tfp = fopen (template, "r")) != NULL) + if ((tfp = CVS_FOPEN (template, "r")) != NULL) { char *line = NULL; size_t line_chars_allocated = 0; @@ -354,60 +498,28 @@ rcsinfo_proc (repository, template) * directory in the source repository. The log information is fed into the * specified program as standard input. */ -static char *title; static FILE *logfp; static char *message; -static char *revision; static List *changes; void -Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges) +Update_Logfile (repository, xmessage, xlogfp, xchanges) char *repository; char *xmessage; - char *xrevision; FILE *xlogfp; List *xchanges; { - char *srepos; - /* nothing to do if the list is empty */ if (xchanges == NULL || xchanges->list->next == xchanges->list) return; /* set up static vars for update_logfile_proc */ message = xmessage; - revision = xrevision; logfp = xlogfp; changes = xchanges; - /* figure out a good title string */ - srepos = Short_Repository (repository); - - /* allocate a chunk of memory to hold the title string */ - if (!str_list) - str_list = xmalloc (MAXLISTLEN); - str_list[0] = '\0'; - - type = T_TITLE; - (void) walklist (changes, title_proc, NULL); - type = T_ADDED; - (void) walklist (changes, title_proc, NULL); - type = T_MODIFIED; - (void) walklist (changes, title_proc, NULL); - type = T_REMOVED; - (void) walklist (changes, title_proc, NULL); - title = xmalloc (strlen (srepos) + strlen (str_list) + 1 + 2); /* for 's */ - (void) sprintf (title, "'%s%s'", srepos, str_list); - - /* to be nice, free up this chunk of memory */ - free (str_list); - str_list = (char *) NULL; - /* call Parse_Info to do the actual logfile updates */ (void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc, 1); - - /* clean up */ - free (title); } /* @@ -418,58 +530,273 @@ update_logfile_proc (repository, filter) char *repository; char *filter; { - return (logfile_write (repository, filter, title, message, revision, - logfp, changes)); + return (logfile_write (repository, filter, message, logfp, changes)); } /* - * concatenate each name onto str_list + * concatenate each filename/version onto str_list */ static int title_proc (p, closure) Node *p; void *closure; { - if (p->data == (char *) type) + struct logfile_info *li; + char *c; + + li = (struct logfile_info *) p->data; + if (li->type == type) { + /* Until we decide on the correct logging solution when we add + directories or perform imports, T_TITLE nodes will only + tack on the name provided, regardless of the format string. + You can verify that this assumption is safe by checking the + code in add.c (add_directory) and import.c (import). */ + + str_list = xrealloc (str_list, strlen (str_list) + 5); (void) strcat (str_list, " "); - (void) strcat (str_list, p->key); + + if (li->type == T_TITLE) + { + str_list = xrealloc (str_list, + strlen (str_list) + strlen (p->key) + 5); + (void) strcat (str_list, p->key); + } + else + { + /* All other nodes use the format string. */ + + for (c = str_list_format; *c != '\0'; c++) + { + switch (*c) + { + case 's': + str_list = + xrealloc (str_list, + strlen (str_list) + strlen (p->key) + 5); + (void) strcat (str_list, p->key); + break; + case 'V': + str_list = + xrealloc (str_list, + (strlen (str_list) + + (li->rev_old ? strlen (li->rev_old) : 0) + + 10) + ); + (void) strcat (str_list, (li->rev_old + ? li->rev_old : "NONE")); + break; + case 'v': + str_list = + xrealloc (str_list, + (strlen (str_list) + + (li->rev_new ? strlen (li->rev_new) : 0) + + 10) + ); + (void) strcat (str_list, (li->rev_new + ? li->rev_new : "NONE")); + break; + /* All other characters, we insert an empty field (but + we do put in the comma separating it from other + fields). This way if future CVS versions add formatting + characters, one can write a loginfo file which at least + won't blow up on an old CVS. */ + } + if (*(c + 1) != '\0') + { + str_list = xrealloc (str_list, strlen (str_list) + 5); + (void) strcat (str_list, ","); + } + } + } } return (0); } /* - * Since some systems don't define this... - */ -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 256 -#endif - -/* * Writes some stuff to the logfile "filter" and returns the status of the * filter program. */ static int -logfile_write (repository, filter, title, message, revision, logfp, changes) +logfile_write (repository, filter, message, logfp, changes) char *repository; char *filter; - char *title; char *message; - char *revision; FILE *logfp; List *changes; { - char cwd[PATH_MAX]; FILE *pipefp; - char *prog = xmalloc (MAXPROGLEN); + char *prog; char *cp; int c; int pipestatus; + char *fmt_percent; /* the location of the percent sign + that starts the format string. */ + + /* The user may specify a format string as part of the filter. + Originally, `%s' was the only valid string. The string that + was substituted for it was: + + <repository-name> <file1> <file2> <file3> ... + + Each file was either a new directory/import (T_TITLE), or a + added (T_ADDED), modified (T_MODIFIED), or removed (T_REMOVED) + file. + + It is desirable to preserve that behavior so lots of commitlog + scripts won't die when they get this new code. At the same + time, we'd like to pass other information about the files (like + version numbers, statuses, or checkin times). + + The solution is to allow a format string that allows us to + specify those other pieces of information. The format string + will be composed of `%' followed by a single format character, + or followed by a set of format characters surrounded by `{' and + `}' as separators. The format characters are: + + s = file name + V = old version number (pre-checkin) + v = new version number (post-checkin) + + For example, valid format strings are: + + %{} + %s + %{s} + %{sVv} + + There's no reason that more items couldn't be added (like + modification date or file status [added, modified, updated, + etc.]) -- the code modifications would be minimal (logmsg.c + (title_proc) and commit.c (check_fileproc)). + + The output will be a string of tokens separated by spaces. For + backwards compatibility, the the first token will be the + repository name. The rest of the tokens will be + comma-delimited lists of the information requested in the + format string. For example, if `/u/src/master' is the + repository, `%{sVv}' is the format string, and three files + (ChangeLog, Makefile, foo.c) were modified, the output might + be: + + /u/src/master ChangeLog,1.1,1.2 Makefile,1.3,1.4 foo.c,1.12,1.13 + + Why this duplicates the old behavior when the format string is + `%s' is left as an exercise for the reader. */ + + fmt_percent = strchr (filter, '%'); + if (fmt_percent) + { + int len; + char *srepos; + char *fmt_begin, *fmt_end; /* beginning and end of the + format string specified in + filter. */ + char *fmt_continue; /* where the string continues + after the format string (we + might skip a '}') somewhere + in there... */ + + /* Grab the format string. */ + + if ((*(fmt_percent + 1) == ' ') || (*(fmt_percent + 1) == '\0')) + { + /* The percent stands alone. This is an error. We could + be treating ' ' like any other formatting character, but + using it as a formatting character seems like it would be + a mistake. */ + + /* Would be nice to also be giving the line number. */ + error (0, 0, "loginfo: '%%' not followed by formatting character"); + fmt_begin = fmt_percent + 1; + fmt_end = fmt_begin; + fmt_continue = fmt_begin; + } + else if (*(fmt_percent + 1) == '{') + { + /* The percent has a set of characters following it. */ + + fmt_begin = fmt_percent + 2; + fmt_end = strchr (fmt_begin, '}'); + if (fmt_end) + { + /* Skip over the '}' character. */ + + fmt_continue = fmt_end + 1; + } + else + { + /* There was no close brace -- assume that format + string continues to the end of the line. */ + + /* Would be nice to also be giving the line number. */ + error (0, 0, "loginfo: '}' missing"); + fmt_end = fmt_begin + strlen (fmt_begin); + fmt_continue = fmt_end; + } + } + else + { + /* The percent has a single character following it. FIXME: + %% should expand to a regular percent sign. */ + + fmt_begin = fmt_percent + 1; + fmt_end = fmt_begin + 1; + fmt_continue = fmt_end; + } + + len = fmt_end - fmt_begin; + str_list_format = xmalloc (sizeof (char) * (len + 1)); + strncpy (str_list_format, fmt_begin, len); + str_list_format[len] = '\0'; + + /* Allocate an initial chunk of memory. As we build up the string + we will realloc it. */ + if (!str_list) + str_list = xmalloc (1); + str_list[0] = '\0'; + + /* Add entries to the string. Don't bother looking for + entries if the format string is empty. */ + + if (str_list_format[0] != '\0') + { + type = T_TITLE; + (void) walklist (changes, title_proc, NULL); + type = T_ADDED; + (void) walklist (changes, title_proc, NULL); + type = T_MODIFIED; + (void) walklist (changes, title_proc, NULL); + type = T_REMOVED; + (void) walklist (changes, title_proc, NULL); + } + + /* Construct the final string. */ + + srepos = Short_Repository (repository); + + prog = xmalloc ((fmt_percent - filter) + strlen (srepos) + + strlen (str_list) + strlen (fmt_continue) + + 10); + (void) strncpy (prog, filter, fmt_percent - filter); + prog[fmt_percent - filter] = '\0'; + (void) strcat (prog, "'"); + (void) strcat (prog, srepos); + (void) strcat (prog, str_list); + (void) strcat (prog, "'"); + (void) strcat (prog, fmt_continue); + + /* To be nice, free up some memory. */ + + free (str_list); + str_list = (char *) NULL; + } + else + { + /* There's no format string. */ + prog = xstrdup (filter); + } - /* - * Only 1 %s argument is supported in the filter - */ - (void) sprintf (prog, filter, title); if ((pipefp = run_popen (prog, "w")) == NULL) { if (!noexec) @@ -478,10 +805,17 @@ logfile_write (repository, filter, title, message, revision, logfp, changes) return (1); } (void) fprintf (pipefp, "Update of %s\n", repository); - (void) fprintf (pipefp, "In directory %s:%s\n\n", hostname, - ((cp = getwd (cwd)) != NULL) ? cp : cwd); - if (revision && *revision) - (void) fprintf (pipefp, "Revision/Branch: %s\n\n", revision); + (void) fprintf (pipefp, "In directory %s:", hostname); + cp = xgetwd (); + if (cp == NULL) + fprintf (pipefp, "<cannot get working directory: %s>\n\n", + strerror (errno)); + else + { + fprintf (pipefp, "%s\n\n", cp); + free (cp); + } + setup_tmpfile (pipefp, "", changes); (void) fprintf (pipefp, "Log Message:\n%s\n", message); if (logfp != (FILE *) 0) @@ -519,3 +853,19 @@ editinfo_proc(repository, editor) editinfo_editor = xstrdup (editor); return (0); } + +/* This routine is calld by Parse_Info. it asigns the name of the + * message verification script to the global variable verify_script + */ +static int +verifymsg_proc (repository, script) + char *repository; + char *script; +{ + if (verifymsg_script && strcmp (verifymsg_script, script) == 0) + return (0); + if (verifymsg_script) + free (verifymsg_script); + verifymsg_script = xstrdup (script); + return (0); +} diff --git a/contrib/cvs/src/modules.c b/contrib/cvs/src/modules.c index 0e07c0b..7c3d495 100644 --- a/contrib/cvs/src/modules.c +++ b/contrib/cvs/src/modules.c @@ -54,17 +54,21 @@ static void save_d PROTO((char *k, int ks, char *d, int ds)); DBM * open_module () { - char mfile[PATH_MAX]; + char *mfile; + DBM *retval; - if (CVSroot == NULL) + if (CVSroot_original == NULL) { - (void) fprintf (stderr, - "%s: must set the CVSROOT environment variable\n", - program_name); - error (1, 0, "or specify the '-d' option to %s", program_name); + error (0, 0, "must set the CVSROOT environment variable"); + error (1, 0, "or specify the '-d' global option"); } - (void) sprintf (mfile, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_MODULES); - return (dbm_open (mfile, O_RDONLY, 0666)); + mfile = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_MODULES) + 20); + (void) sprintf (mfile, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_MODULES); + retval = dbm_open (mfile, O_RDONLY, 0666); + free (mfile); + return retval; } /* @@ -103,42 +107,57 @@ do_module (db, mname, m_type, msg, callback_proc, where, char *tag_prog = NULL; char *update_prog = NULL; struct saved_cwd cwd; + int cwd_saved = 0; char *line; int modargc; int xmodargc; char **modargv; - char *xmodargv[MAXFILEPERDIR]; + char **xmodargv; char *value; - char *zvalue; + char *zvalue = NULL; char *mwhere = NULL; char *mfile = NULL; char *spec_opt = NULL; - char xvalue[PATH_MAX]; + char *xvalue = NULL; int alias = 0; datum key, val; char *cp; int c, err = 0; + int nonalias_opt = 0; #ifdef SERVER_SUPPORT + int restore_server_dir = 0; + char *server_dir_to_restore; if (trace) - { - fprintf (stderr, "%s%c-> do_module (%s, %s, %s, %s)\n", - error_use_protocol ? "E " : "", + { + char *buf; + + /* We use cvs_outerr, rather than fprintf to stderr, because + this may be called by server code with error_use_protocol + set. */ + buf = xmalloc (100 + + strlen (mname) + + strlen (msg) + + (where ? strlen (where) : 0) + + (extra_arg ? strlen (extra_arg) : 0)); + sprintf (buf, "%c-> do_module (%s, %s, %s, %s)\n", (server_active) ? 'S' : ' ', mname, msg, where ? where : "", extra_arg ? extra_arg : ""); - } + cvs_outerr (buf, 0); + free (buf); + } #endif /* if this is a directory to ignore, add it to that list */ if (mname[0] == '!' && mname[1] != '\0') { ign_dir_add (mname+1); - return(err); + goto do_module_return; } /* strip extra stuff from the module name */ - strip_path (mname); + strip_trailing_slashes (mname); /* * Look up the module using the following scheme: @@ -182,28 +201,31 @@ do_module (db, mname, m_type, msg, callback_proc, where, } else { - char file[PATH_MAX]; - char attic_file[PATH_MAX]; + char *file; + char *attic_file; char *acp; + int is_found = 0; /* check to see if mname is a directory or file */ - - (void) sprintf (file, "%s/%s", CVSroot, mname); + file = xmalloc (strlen (CVSroot_directory) + strlen (mname) + 10); + (void) sprintf (file, "%s/%s", CVSroot_directory, mname); + attic_file = xmalloc (strlen (CVSroot_directory) + strlen (mname) + + sizeof (CVSATTIC) + sizeof (RCSEXT) + 15); if ((acp = strrchr (mname, '/')) != NULL) { *acp = '\0'; - (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot, mname, - CVSATTIC, acp + 1, RCSEXT); + (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot_directory, + mname, CVSATTIC, acp + 1, RCSEXT); *acp = '/'; } else - (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot, CVSATTIC, - mname, RCSEXT); + (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot_directory, + CVSATTIC, mname, RCSEXT); if (isdir (file)) { value = mname; - goto found; + is_found = 1; } else { @@ -215,7 +237,9 @@ do_module (db, mname, m_type, msg, callback_proc, where, { char *slashp; - /* put the ' ' in a copy so we don't mess up the original */ + /* put the ' ' in a copy so we don't mess up the + original */ + xvalue = xmalloc (strlen (mname) + 2); value = strcpy (xvalue, mname); slashp = strrchr (value, '/'); *slashp = ' '; @@ -230,19 +254,26 @@ do_module (db, mname, m_type, msg, callback_proc, where, if (cp == mname) { /* drop the leading / if specified */ + xvalue = xmalloc (strlen (mname) + 10); value = strcpy (xvalue, ". "); (void) strcat (xvalue, mname + 1); } else { /* otherwise just copy it */ + xvalue = xmalloc (strlen (mname) + 10); value = strcpy (xvalue, ". "); (void) strcat (xvalue, mname); } } - goto found; + is_found = 1; } } + free (attic_file); + free (file); + + if (is_found) + goto found; } /* look up everything to the first / as a module */ @@ -293,9 +324,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* if we got here, we couldn't find it using our search, so give up */ error (0, 0, "cannot find module `%s' - ignored", mname); err++; - if (mwhere) - free (mwhere); - return (err); + goto do_module_return; /* @@ -306,7 +335,8 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* remember where we start */ if (save_cwd (&cwd)) - exit (EXIT_FAILURE); + error_exit (); + cwd_saved = 1; /* copy value to our own string since if we go recursive we'll be really screwed if we do another dbm lookup */ @@ -343,7 +373,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* XXX - think about making null repositories at each dir here instead of just at the bottom */ make_directories (dir); - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) { error (0, errno, "cannot chdir to %s", dir); spec_opt = NULL; @@ -352,9 +382,13 @@ do_module (db, mname, m_type, msg, callback_proc, where, } if (!isfile (CVSADM)) { - char nullrepos[PATH_MAX]; + char *nullrepos; - (void) sprintf (nullrepos, "%s/%s/%s", CVSroot, + nullrepos = xmalloc (strlen (CVSroot_directory) + + sizeof (CVSROOTADM) + + sizeof (CVSNULLREPOS) + + 10); + (void) sprintf (nullrepos, "%s/%s/%s", CVSroot_directory, CVSROOTADM, CVSNULLREPOS); if (!isfile (nullrepos)) { @@ -367,7 +401,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, error (1, 0, "there is no repository %s", nullrepos); Create_Admin (".", dir, - nullrepos, (char *) NULL, (char *) NULL); + nullrepos, (char *) NULL, (char *) NULL, 0); if (!noexec) { FILE *fp; @@ -380,6 +414,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, server_set_entstat (dir, nullrepos); #endif } + free (nullrepos); } out: goto do_special; @@ -404,7 +439,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, (void) sprintf (line, "%s %s", "XXX", value); /* turn the line into an argv[] array */ - line2argv (&xmodargc, xmodargv, line); + line2argv (&xmodargc, &xmodargv, line); free (line); modargc = xmodargc; modargv = xmodargv; @@ -419,26 +454,33 @@ do_module (db, mname, m_type, msg, callback_proc, where, alias = 1; break; case 'd': + nonalias_opt = 1; if (mwhere) free (mwhere); mwhere = xstrdup (optarg); break; case 'i': + nonalias_opt = 1; checkin_prog = optarg; break; case 'l': + nonalias_opt = 1; local_specified = 1; break; case 'o': + nonalias_opt = 1; checkout_prog = optarg; break; case 'e': + nonalias_opt = 1; export_prog = optarg; break; case 't': + nonalias_opt = 1; tag_prog = optarg; break; case 'u': + nonalias_opt = 1; update_prog = optarg; break; case '?': @@ -446,11 +488,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, "modules file has invalid option for key %s value %s", key.dptr, val.dptr); err++; - if (mwhere) - free (mwhere); - free (zvalue); - free_cwd (&cwd); - return (err); + goto do_module_return; } } modargc -= optind; @@ -458,11 +496,20 @@ do_module (db, mname, m_type, msg, callback_proc, where, if (modargc == 0) { error (0, 0, "modules file missing directory for module %s", mname); - if (mwhere) - free (mwhere); - free (zvalue); - free_cwd (&cwd); - return (++err); + ++err; + goto do_module_return; + } + + if (alias && nonalias_opt) + { + /* The documentation has never said it is legal to specify + -a along with another option. And I believe that in the past + CVS has ignored the options other than -a, more or less, in this + situation. */ + error (0, 0, "\ +-a cannot be specified in the modules file along with other options"); + ++err; + goto do_module_return; } /* if this was an alias, call ourselves recursively for each module */ @@ -481,11 +528,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, where, shorten, local_specified, run_module_prog, extra_arg); } - if (mwhere) - free (mwhere); - free (zvalue); - free_cwd (&cwd); - return (err); + goto do_module_return; } /* otherwise, process this module */ @@ -507,6 +550,40 @@ do_module (db, mname, m_type, msg, callback_proc, where, if (local_specified) spec_opt = NULL; +#ifdef SERVER_SUPPORT + /* We want to check out into the directory named by the module. + So we set a global variable which tells the server to glom that + directory name onto the front. A cleaner approach would be some + way of passing it down to the recursive call, through the + callback_proc, to start_recursion, and then into the update_dir in + the struct file_info. That way the "Updating foo" message could + print the actual directory we are checking out into. + + For local CVS, this is handled by the chdir call above + (directly or via the callback_proc). */ + if (server_active && spec_opt != NULL) + { + char *change_to; + + change_to = where ? where : (mwhere ? mwhere : mname); + server_dir_to_restore = server_dir; + restore_server_dir = 1; + server_dir = + xmalloc ((server_dir_to_restore != NULL + ? strlen (server_dir_to_restore) + : 0) + + strlen (change_to) + + 5); + server_dir[0] = '\0'; + if (server_dir_to_restore != NULL) + { + strcat (server_dir, server_dir_to_restore); + strcat (server_dir, "/"); + } + strcat (server_dir, change_to); + } +#endif + while (spec_opt != NULL) { char *next_opt; @@ -539,6 +616,14 @@ do_module (db, mname, m_type, msg, callback_proc, where, spec_opt = next_opt; } +#ifdef SERVER_SUPPORT + if (server_active && restore_server_dir) + { + free (server_dir); + server_dir = server_dir_to_restore; + } +#endif + /* write out the checkin/update prog files if necessary */ #ifdef SERVER_SUPPORT if (err == 0 && !noexec && m_type == CHECKOUT && server_expanding) @@ -572,8 +657,9 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* cd back to where we started */ if (restore_cwd (&cwd, NULL)) - exit (EXIT_FAILURE); + error_exit (); free_cwd (&cwd); + cwd_saved = 0; /* run checkout or tag prog if appropriate */ if (err == 0 && run_module_prog) @@ -588,38 +674,57 @@ do_module (db, mname, m_type, msg, callback_proc, where, * if we can't find a matching program, just punt and use * whatever is specified in the modules file. */ - char real_prog[PATH_MAX]; + char *real_prog = NULL; char *prog = (m_type == TAG ? tag_prog : (m_type == CHECKOUT ? checkout_prog : export_prog)); char *real_where = (where != NULL ? where : mwhere); + char *expanded_path; if ((*prog != '/') && (*prog != '.')) { + real_prog = xmalloc (strlen (real_where) + strlen (prog) + + 10); (void) sprintf (real_prog, "%s/%s", real_where, prog); if (isfile (real_prog)) prog = real_prog; } - run_setup ("%s %s", prog, real_where); - if (extra_arg) - run_arg (extra_arg); - - if (!quiet) + /* XXX can we determine the line number for this entry??? */ + expanded_path = expand_path (prog, "modules", 0); + if (expanded_path != NULL) { - (void) printf ("%s %s: Executing '", program_name, - command_name); - run_print (stdout); - (void) printf ("'\n"); + run_setup ("%s %s", expanded_path, real_where); + + if (extra_arg) + run_arg (extra_arg); + + if (!quiet) + { + cvs_output (program_name, 0); + cvs_output (" ", 1); + cvs_output (command_name, 0); + cvs_output (": Executing '", 0); + run_print (stdout); + cvs_output ("'\n", 0); + } + err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + free (expanded_path); } - err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + free (real_prog); } } + do_module_return: /* clean up */ if (mwhere) free (mwhere); - free (zvalue); + if (cwd_saved) + free_cwd (&cwd); + if (zvalue != NULL) + free (zvalue); + if (xvalue != NULL) + free (xvalue); return (err); } @@ -774,24 +879,7 @@ cat_module (status) int moduleargc; struct sortrec *s_h; char *cp, *cp2, **argv; - char *line; - char *moduleargv[MAXFILEPERDIR]; - -#ifdef sun -#ifdef TIOCGSIZE - struct ttysize ts; - - (void) ioctl (0, TIOCGSIZE, &ts); - cols = ts.ts_cols; -#endif -#else -#ifdef TIOCGWINSZ - struct winsize ws; - - (void) ioctl (0, TIOCGWINSZ, &ws); - cols = ws.ws_col; -#endif -#endif + char **moduleargv; Status = status; @@ -806,6 +894,8 @@ cat_module (status) save_d (key.dptr, key.dsize, val.dptr, val.dsize); } + close_module (db); + /* Sort the list as requested */ qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order); @@ -817,19 +907,27 @@ cat_module (status) fill = cols - (indent + 2); for (s_h = s_head, i = 0; i < s_count; i++, s_h++) { + char *line; + /* Print module name (and status, if wanted) */ - (void) printf ("%-12s", s_h->modname); + line = xmalloc (strlen (s_h->modname) + 15); + sprintf (line, "%-12s", s_h->modname); + cvs_output (line, 0); + free (line); if (status) { - (void) printf (" %-11s", s_h->status); + line = xmalloc (strlen (s_h->status) + 15); + sprintf (line, " %-11s", s_h->status); + cvs_output (line, 0); if (s_h->status != def_status) *(s_h->status + strlen (s_h->status)) = ' '; + free (line); } + line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 15); /* Parse module file entry as command line and print options */ - line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 10); (void) sprintf (line, "%s %s", s_h->modname, s_h->rest); - line2argv (&moduleargc, moduleargv, line); + line2argv (&moduleargc, &moduleargv, line); free (line); argc = moduleargc; argv = moduleargv; @@ -842,17 +940,28 @@ cat_module (status) { if (c == 'a' || c == 'l') { - (void) printf (" -%c", c); + char buf[5]; + + sprintf (buf, " -%c", c); + cvs_output (buf, 0); wid += 3; /* Could just set it to 3 */ } else { + char buf[10]; + if (strlen (optarg) + 4 + wid > (unsigned) fill) { - (void) printf ("\n%*s", indent, ""); + int j; + + cvs_output ("\n", 1); + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); wid = 0; } - (void) printf (" -%c %s", c, optarg); + sprintf (buf, " -%c ", c); + cvs_output (buf, 0); + cvs_output (optarg, 0); wid += strlen (optarg) + 4; } } @@ -865,21 +974,31 @@ cat_module (status) { if (strlen (*argv) + wid > (unsigned) fill) { - (void) printf ("\n%*s", indent, ""); + int j; + + cvs_output ("\n", 1); + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); wid = 0; } - (void) printf (" %s", *argv); + cvs_output (" ", 1); + cvs_output (*argv, 0); wid += strlen (*argv) + 1; } - (void) printf ("\n"); + cvs_output ("\n", 1); /* Format the comment field -- save_d (), compressed spaces */ for (cp2 = cp = s_h->comment; *cp; cp2 = cp) { - (void) printf ("%*s # ", indent, ""); + int j; + + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); + cvs_output (" # ", 0); if (strlen (cp2) < (unsigned) (fill - 2)) { - (void) printf ("%s\n", cp2); + cvs_output (cp2, 0); + cvs_output ("\n", 1); break; } cp += fill - 2; @@ -887,12 +1006,14 @@ cat_module (status) cp--; if (cp == cp2) { - (void) printf ("%s\n", cp2); + cvs_output (cp2, 0); + cvs_output ("\n", 1); break; } *cp++ = '\0'; - (void) printf ("%s\n", cp2); + cvs_output (cp2, 0); + cvs_output ("\n", 1); } free_names(&moduleargc, moduleargv); diff --git a/contrib/cvs/src/myndbm.c b/contrib/cvs/src/myndbm.c index 527f7ee..1a3fcae 100644 --- a/contrib/cvs/src/myndbm.c +++ b/contrib/cvs/src/myndbm.c @@ -19,7 +19,7 @@ #ifdef MY_NDBM -static void mydbm_load_file (); +static void mydbm_load_file PROTO ((FILE *, List *)); /* ARGSUSED */ DBM * @@ -31,7 +31,7 @@ mydbm_open (file, flags, mode) FILE *fp; DBM *db; - fp = fopen (file, FOPEN_BINARY_READ); + fp = CVS_FOPEN (file, FOPEN_BINARY_READ); if (fp == NULL && !(existence_error (errno) && (flags & O_CREAT))) return ((DBM *) 0); @@ -71,7 +71,7 @@ mydbm_close (db) if (db->modified) { FILE *fp; - fp = fopen (db->name, FOPEN_BINARY_WRITE); + fp = CVS_FOPEN (db->name, FOPEN_BINARY_WRITE); if (fp == NULL) error (1, errno, "cannot write %s", db->name); walklist (db->dbm_list, write_item, (void *)fp); @@ -199,11 +199,14 @@ mydbm_load_file (fp, list) { char *line = NULL; size_t line_len; - /* FIXME: arbitrary limit. */ - char value[MAXLINELEN]; + char *value; + size_t value_allocated; char *cp, *vp; int len, cont; + value_allocated = 1; + value = xmalloc (value_allocated); + for (cont = 0; getline (&line, &line_len, fp) >= 0;) { if ((cp = strrchr (line, '\012')) != NULL) @@ -221,9 +224,8 @@ mydbm_load_file (fp, list) * line; otherwise at the beginning, but only after any trailing * backslash is removed. */ - vp = value; - if (cont) - vp += strlen (value); + if (!cont) + value[0] = '\0'; /* * See if the line we read is a continuation line, and strip the @@ -243,7 +245,11 @@ mydbm_load_file (fp, list) { cont = 0; } - (void) strcpy (vp, line); + expand_string (&value, + &value_allocated, + strlen (value) + strlen (line) + 5); + strcat (value, line); + if (value[0] == '#') continue; /* comment line */ vp = value; @@ -283,6 +289,7 @@ mydbm_load_file (fp, list) } } free (line); + free (value); } #endif /* MY_NDBM */ diff --git a/contrib/cvs/src/myndbm.h b/contrib/cvs/src/myndbm.h index 0431e15..2bce739 100644 --- a/contrib/cvs/src/myndbm.h +++ b/contrib/cvs/src/myndbm.h @@ -1,5 +1,3 @@ -/* $CVSid: @(#)myndbm.h 1.4 94/09/21 $ */ - #ifdef MY_NDBM #define DBLKSIZ 4096 diff --git a/contrib/cvs/src/no_diff.c b/contrib/cvs/src/no_diff.c index a0d00f5..384800f 100644 --- a/contrib/cvs/src/no_diff.c +++ b/contrib/cvs/src/no_diff.c @@ -17,20 +17,22 @@ #include "cvs.h" int -No_Difference (file, vers, entries, repository, update_dir) - char *file; +No_Difference (finfo, vers) + struct file_info *finfo; Vers_TS *vers; - List *entries; - char *repository; - char *update_dir; { Node *p; - char tmp[L_tmpnam+1]; int ret; char *ts, *options; int retcode = 0; char *tocvsPath; + /* If ts_user is "Is-modified", we can only conclude the files are + different (since we don't have the file's contents). */ + if (vers->ts_user != NULL + && strcmp (vers->ts_user, "Is-modified") == 0) + return -1; + if (!vers->srcfile || !vers->srcfile->path) return (-1); /* different since we couldn't tell */ @@ -39,91 +41,51 @@ No_Difference (file, vers, entries, repository, update_dir) else options = xstrdup (""); - retcode = RCS_checkout (vers->srcfile->path, NULL, vers->vn_user, options, - tmpnam (tmp), 0, 0); + tocvsPath = wrap_tocvs_process_file (finfo->file); + retcode = RCS_cmp_file (vers->srcfile, vers->vn_user, options, + tocvsPath == NULL ? finfo->file : tocvsPath); if (retcode == 0) { -#if 0 - /* Why would we want to munge the modes? And only if the timestamps - are different? And even for commands like "cvs status"???? */ - if (!iswritable (file)) /* fix the modes as a side effect */ - xchmod (file, 1); -#endif - - tocvsPath = wrap_tocvs_process_file (file); - - /* do the byte by byte compare */ - if (xcmp (tocvsPath == NULL ? file : tocvsPath, tmp) == 0) - { -#if 0 - /* Why would we want to munge the modes? And only if the - timestamps are different? And even for commands like - "cvs status"???? */ - if (cvswrite == FALSE) /* fix the modes as a side effect */ - xchmod (file, 0); -#endif - - /* no difference was found, so fix the entries file */ - ts = time_stamp (file); - Register (entries, file, - vers->vn_user ? vers->vn_user : vers->vn_rcs, ts, - options, vers->tag, vers->date, (char *) 0); + /* no difference was found, so fix the entries file */ + ts = time_stamp (finfo->file); + Register (finfo->entries, finfo->file, + vers->vn_user ? vers->vn_user : vers->vn_rcs, ts, + options, vers->tag, vers->date, (char *) 0); #ifdef SERVER_SUPPORT - if (server_active) - { - /* We need to update the entries line on the client side. */ - server_update_entries - (file, update_dir, repository, SERVER_UPDATED); - } + if (server_active) + { + /* We need to update the entries line on the client side. */ + server_update_entries + (finfo->file, finfo->update_dir, finfo->repository, SERVER_UPDATED); + } #endif - free (ts); + free (ts); - /* update the entdata pointer in the vers_ts structure */ - p = findnode (entries, file); - vers->entdata = (Entnode *) p->data; + /* update the entdata pointer in the vers_ts structure */ + p = findnode (finfo->entries, finfo->file); + vers->entdata = (Entnode *) p->data; - ret = 0; - } - else - ret = 1; /* files were really different */ - if (tocvsPath) - { - /* Need to call unlink myself because the noexec variable - * has been set to 1. */ - if (trace) - (void) fprintf (stderr, "%c-> unlink (%s)\n", -#ifdef SERVER_SUPPORT - (server_active) ? 'S' : ' ', -#else - ' ', -#endif - tocvsPath); - if (unlink (tocvsPath) < 0) - error (0, errno, "could not remove %s", tocvsPath); - } + ret = 0; } else - { - if (update_dir[0] == '\0') - error (0, retcode == -1 ? errno : 0, - "could not check out revision %s of %s", - vers->vn_user, file); - else - error (0, retcode == -1 ? errno : 0, - "could not check out revision %s of %s/%s", - vers->vn_user, update_dir, file); - ret = -1; /* different since we couldn't tell */ - } + ret = 1; /* files were really different */ - if (trace) + if (tocvsPath) + { + /* Need to call unlink myself because the noexec variable + * has been set to 1. */ + if (trace) + (void) fprintf (stderr, "%c-> unlink (%s)\n", #ifdef SERVER_SUPPORT - (void) fprintf (stderr, "%c-> unlink2 (%s)\n", - (server_active) ? 'S' : ' ', tmp); + (server_active) ? 'S' : ' ', #else - (void) fprintf (stderr, "-> unlink (%s)\n", tmp); + ' ', #endif - if (unlink (tmp) < 0) - error (0, errno, "could not remove %s", tmp); + tocvsPath); + if ( CVS_UNLINK (tocvsPath) < 0) + error (0, errno, "could not remove %s", tocvsPath); + } + free (options); return (ret); } diff --git a/contrib/cvs/src/options.h.in b/contrib/cvs/src/options.h.in index 7cb58dc..476d747 100644 --- a/contrib/cvs/src/options.h.in +++ b/contrib/cvs/src/options.h.in @@ -16,70 +16,47 @@ */ /* - * CVS provides the most features when used in conjunction with the Version-5 - * release of RCS. Thus, it is the default. This also assumes that GNU diff - * Version-1.15 is being used as well -- you will have to configure your RCS - * V5 release separately to make this the case. If you do not have RCS V5 and - * GNU diff V1.15, comment out this define. You should not try mixing and - * matching other combinations of these tools. + * CVS provides the most features when used in conjunction with the + * Version-5 release of RCS. Thus, it is the default. This also + * assumes that GNU diff Version-1.15 is being used as well -- you + * will have to configure your RCS V5 release separately to make this + * the case. If you do not have RCS V5 and GNU diff V1.15, comment out + * this define. You should not try mixing and matching other + * combinations of these tools. */ #ifndef HAVE_RCS5 #define HAVE_RCS5 #endif /* - * If, before installing this version of CVS, you were running RCS V4 AND you - * are installing this CVS and RCS V5 and GNU diff 1.15 all at the same time, - * you should turn on the following define. It only exists to try to do - * reasonable things with your existing checked out files when you upgrade to - * RCS V5, since the keyword expansion formats have changed with RCS V5. - * - * If you already have been running with RCS5, or haven't been running with CVS - * yet at all, or are sticking with RCS V4 for now, leave the commented out. - */ -#ifndef HAD_RCS4 -/* #define HAD_RCS4 */ -#endif - -/* - * For portability and heterogeneity reasons, CVS is shipped by default using - * my own text-file version of the ndbm database library in the src/myndbm.c - * file. If you want better performance and are not concerned about - * heterogeneous hosts accessing your modules file, turn this option off. + * For portability and heterogeneity reasons, CVS is shipped by + * default using my own text-file version of the ndbm database library + * in the src/myndbm.c file. If you want better performance and are + * not concerned about heterogeneous hosts accessing your modules + * file, turn this option off. */ #ifndef MY_NDBM #define MY_NDBM #endif /* - * The "diff" program to execute when creating patch output. This "diff" - * must support the "-c" option for context diffing. Specify a full - * pathname if your site wants to use a particular diff. Note that unlike - * the diff used with RCS, you *must not* supply -a here (doing so will cause - * the server to generate patches which patch cannot handle in some cases). + * The "diff" program to execute when creating patch output. This + * "diff" must support the "-c" option for context diffing. Specify a + * full pathname if your site wants to use a particular diff. Note + * that unlike the diff used with RCS, you *must not* supply -a here + * (doing so will cause the server to generate patches which patch + * cannot handle in some cases). * * NOTE: this program is only used for the ``patch'' sub-command (and * for ``update'' if you are using the server). The other commands * use rcsdiff which will use whatever version of diff was specified * when rcsdiff was built on your system. */ - #ifndef DIFF #define DIFF "diff" #endif /* - * The "grep" program to execute when checking to see if a merged file had - * any conflicts. This "grep" must support a standard basic - * regular expression as an argument. Specify a full pathname if your site - * wants to use a particular grep. - */ - -#ifndef GREP -#define GREP "grep" -#endif - -/* * The "patch" program to run when using the CVS server and accepting * patches across the network. Specify a full pathname if your site * wants to use a particular patch. @@ -89,13 +66,13 @@ #endif /* - * By default, RCS programs are executed with the shell or through execlp(), - * so the user's PATH environment variable is searched. If you'd like to - * bind all RCS programs to a certain directory (perhaps one not in most - * people's PATH) then set the default in RCSBIN_DFLT. Note that setting - * this here will cause all RCS programs to be executed from this directory, - * unless the user overrides the default with the RCSBIN environment variable - * or the "-b" option to CVS. + * By default, RCS programs are executed with the shell or through + * execlp(), so the user's PATH environment variable is searched. If + * you'd like to bind all RCS programs to a certain directory (perhaps + * one not in most people's PATH) then set the default in RCSBIN_DFLT. + * Note that setting this here will cause all RCS programs to be + * executed from this directory, unless the user overrides the default + * with the RCSBIN environment variable or the "-b" option to CVS. * * If you use the password-authenticating server, then you need to * make sure that the server can find the RCS programs to invoke them. @@ -104,12 +81,16 @@ * complete. But no actual shell is ever started by that user, so the * PATH environment variable may not contain the directory with the * RCS binaries, even though if that user logged in normally, PATH - * would include the directory. + * would include the directory. * * One way to solve this problem is to set RCSBIN_DFLT here. An * alternative is to make sure that root has the right directory in * its path already. Another, probably better alternative is to - * specify -b in /etc/inetd.conf. + * specify -b in /etc/inetd.conf. + * + * You may also have to set RCSBIN_DFLT here if there's no global + * start-up script run for users by rshd and your RCS programs are not + * in a directory in the default PATH assigned by rshd. * * This define should be either the empty string ("") or a full * pathname to the directory containing all the installed programs @@ -119,12 +100,20 @@ #define RCSBIN_DFLT "" #endif +/* Directory used for storing temporary files, if not overridden by + environment variables or the -T global option. There should be little + need to change this (-T is a better mechanism if you need to use a + different directory for temporary files). */ +#ifndef TMPDIR_DFLT +#define TMPDIR_DFLT "/tmp" +#endif + /* - * The default editor to use, if one does not specify the "-e" option to cvs, - * or does not have an EDITOR environment variable. I set this to just "vi", - * and use the shell to find where "vi" actually is. This allows sites with - * /usr/bin/vi or /usr/ucb/vi to work equally well (assuming that your PATH - * is reasonable). + * The default editor to use, if one does not specify the "-e" option + * to cvs, or does not have an EDITOR environment variable. I set + * this to just "vi", and use the shell to find where "vi" actually + * is. This allows sites with /usr/bin/vi or /usr/ucb/vi to work + * equally well (assuming that your PATH is reasonable). */ #ifndef EDITOR_DFLT #define EDITOR_DFLT "vi" @@ -147,22 +136,23 @@ * The cvs admin command is restricted to the members of the group * CVS_ADMIN_GROUP. If this group does not exist, all users are * allowed to run cvs admin. To disable the cvs admin for all users, - * create an empty group CVS_ADMIN_GROUP. To disable access control for - * cvs admin, comment out the define below. + * create an empty group CVS_ADMIN_GROUP. To disable access control + * for cvs admin, comment out the define below. */ #ifndef CVS_ADMIN_GROUP #define CVS_ADMIN_GROUP "cvsadmin" #endif /* - * The Repository file holds the path to the directory within the source - * repository that contains the RCS ,v files for each CVS working directory. - * This path is either a full-path or a path relative to CVSROOT. + * The Repository file holds the path to the directory within the + * source repository that contains the RCS ,v files for each CVS + * working directory. This path is either a full-path or a path + * relative to CVSROOT. * - * The only advantage that I can see to having a relative path is that One can - * change the physical location of the master source repository, change one's - * CVSROOT environment variable, and CVS will work without problems. I - * recommend using full-paths. + * The only advantage that I can see to having a relative path is that + * one can change the physical location of the master source + * repository, change one's CVSROOT environment variable, and CVS will + * work without problems. I recommend using full-paths. */ #ifndef RELATIVE_REPOS /* #define RELATIVE_REPOS */ @@ -170,25 +160,28 @@ /* * When committing or importing files, you must enter a log message. - * Normally, you can do this either via the -m flag on the command line or an - * editor will be started for you. If you like to use logging templates (the - * rcsinfo file within the $CVSROOT/CVSROOT directory), you might want to - * force people to use the editor even if they specify a message with -m. - * Enabling FORCE_USE_EDITOR will cause the -m message to be appended to the - * temp file when the editor is started. + * Normally, you can do this either via the -m flag on the command + * line or an editor will be started for you. If you like to use + * logging templates (the rcsinfo file within the $CVSROOT/CVSROOT + * directory), you might want to force people to use the editor even + * if they specify a message with -m. Enabling FORCE_USE_EDITOR will + * cause the -m message to be appended to the temp file when the + * editor is started. */ #ifndef FORCE_USE_EDITOR /* #define FORCE_USE_EDITOR */ #endif /* - * When locking the repository, some sites like to remove locks and assume - * the program that created them went away if the lock has existed for a long - * time. This used to be the default for previous versions of CVS. CVS now - * attempts to be much more robust, so lock files should not be left around - * by mistake. The new behaviour will never remove old locks (they must now - * be removed by hand). Enabling CVS_FUDGELOCKS will cause CVS to remove - * locks that are older than CVSLCKAGE seconds. + * When locking the repository, some sites like to remove locks and + * assume the program that created them went away if the lock has + * existed for a long time. This used to be the default for previous + * versions of CVS. CVS now attempts to be much more robust, so lock + * files should not be left around by mistake. The new behaviour will + * never remove old locks (they must now be removed by hand). + * Enabling CVS_FUDGELOCKS will cause CVS to remove locks that are + * older than CVSLCKAGE seconds. + * * Use of this option is NOT recommended. */ #ifndef CVS_FUDGELOCKS @@ -197,30 +190,18 @@ /* * When committing a permanent change, CVS and RCS make a log entry of - * who committed the change. If you are committing the change logged in - * as "root" (not under "su" or other root-priv giving program), CVS/RCS - * cannot determine who is actually making the change. + * who committed the change. If you are committing the change logged + * in as "root" (not under "su" or other root-priv giving program), + * CVS/RCS cannot determine who is actually making the change. * * As such, by default, CVS disallows changes to be committed by users - * logged in as "root". You can disable this option by commenting - * out the lines below. + * logged in as "root". You can disable this option by commenting out + * the lines below. */ #ifndef CVS_BADROOT #define CVS_BADROOT #endif -/* - * The "cvs diff" command accepts all the single-character options that GNU - * diff (1.15) accepts. Except -D. GNU diff uses -D as a way to put - * cpp-style #define's around the output differences. CVS, by default, uses - * -D to specify a free-form date (like "cvs diff -D '1 week ago'"). If - * you would prefer that the -D option of "cvs diff" work like the GNU diff - * option, then comment out this define. - */ -#ifndef CVS_DIFFDATE -#define CVS_DIFFDATE -#endif - /* Define this to enable the SETXID support. The way to use this is to create a group with no users in it (except perhaps cvs administrators), set the cvs executable to setgid that group, chown @@ -228,42 +209,54 @@ permissions in the repository to 770. The last person to modify a file will own it, but as long as directory permissions are set right that won't matter. You'll need a system which inherits file - groups from the parent directory. I don't know how carefully this - has been inspected for security holes. */ + groups from the parent directory (WARNING: using the wrong kind of + system (I think Solaris 2.4 is the wrong kind, for example) will + create a security hole! You will receive no warning other than the + fact that files in the working directory are owned by the group + which cvs is setgid to). + One security hole which has been reported is that setgid is not + turned off when the editor is invoked--most editors provide a way + to execute a shell, or the user can specify an editor (this one is + large enough to drive a truck through). Don't assume that the + holes described here are the only ones; I don't know how carefully + SETXID has been inspected for security holes. */ #ifndef SETXID_SUPPORT /* #define SETXID_SUPPORT */ #endif -/* Should we build the password-authenticating client? Whether to - include the password-authenticating _server_, on the other hand, is - set in config.h. */ +/* + * Should we build the password-authenticating client? Whether to + * include the password-authenticating _server_, on the other hand, is + * set in config.h. + */ +#ifdef CLIENT_SUPPORT #define AUTH_CLIENT_SUPPORT 1 +#endif /* - * If you are working with a large remote repository and a 'cvs checkout' is - * swamping your network and memory, define these to enable flow control. - * You will end up with even less guarantees of a consistant checkout, - * but that may be better than no checkout at all. The master server process - * will monitor how far it is getting behind, if it reaches the high water - * mark, it will signal the child process to stop generating data when - * convenient (ie: no locks are held, currently at the beginning of a - * new directory). Once the buffer has drained sufficiently to reach the - * low water mark, it will be signalled to start again. - * -- EXPERIMENTAL! -- A better solution may be in the works. - * You may override the default hi/low watermarks here too. + * If you are working with a large remote repository and a 'cvs + * checkout' is swamping your network and memory, define these to + * enable flow control. You will end up with even less probability of + * a consistent checkout (see Concurrency in cvs.texinfo), but CVS + * doesn't try to guarantee that anyway. The master server process + * will monitor how far it is getting behind, if it reaches the high + * water mark, it will signal the child process to stop generating + * data when convenient (ie: no locks are held, currently at the + * beginning of a new directory). Once the buffer has drained + * sufficiently to reach the low water mark, it will be signalled to + * start again. You may override the default hi/low watermarks here + * too. */ -#ifndef SERVER_FLOWCONTROL -/* #define SERVER_FLOWCONTROL */ -/* #define SERVER_HI_WATER (2 * 1024 * 1024) */ -/* #define SERVER_LO_WATER (1 * 1024 * 1024) */ -#endif +#define SERVER_FLOWCONTROL +#define SERVER_HI_WATER (2 * 1024 * 1024) +#define SERVER_LO_WATER (1 * 1024 * 1024) /* End of CVS configuration section */ /* - * Externs that are included in libc, but are used frequently enough to - * warrant defining here. + * Externs that are included in libc, but are used frequently enough + * to warrant defining here. */ #ifndef STDC_HEADERS extern void exit (); diff --git a/contrib/cvs/src/parseinfo.c b/contrib/cvs/src/parseinfo.c index c567ef8..1011965 100644 --- a/contrib/cvs/src/parseinfo.c +++ b/contrib/cvs/src/parseinfo.c @@ -7,6 +7,7 @@ */ #include "cvs.h" +#include "getline.h" /* * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for @@ -24,15 +25,16 @@ Parse_Info (infofile, repository, callproc, all) { int err = 0; FILE *fp_info; - char infopath[PATH_MAX]; - char line[MAXLINELEN]; + char *infopath; + char *line = NULL; + size_t line_allocated = 0; char *default_value = NULL; char *expanded_value= NULL; int callback_done, line_number; char *cp, *exp, *value, *srepos; const char *regex_err; - if (CVSroot == NULL) + if (CVSroot_original == NULL) { /* XXX - should be error maybe? */ error (0, 0, "CVSROOT variable not set"); @@ -40,10 +42,20 @@ Parse_Info (infofile, repository, callproc, all) } /* find the info file and open it */ - (void) sprintf (infopath, "%s/%s/%s", CVSroot, + infopath = xmalloc (strlen (CVSroot_directory) + + strlen (infofile) + + sizeof (CVSROOTADM) + + 10); + (void) sprintf (infopath, "%s/%s/%s", CVSroot_directory, CVSROOTADM, infofile); - if ((fp_info = fopen (infopath, "r")) == NULL) - return (0); /* no file -> nothing special done */ + fp_info = CVS_FOPEN (infopath, "r"); + if (fp_info == NULL) + { + /* If no file, don't do anything special. */ + if (!existence_error (errno)) + error (0, errno, "cannot open %s", infopath); + return 0; + } /* strip off the CVSROOT if repository was absolute */ srepos = Short_Repository (repository); @@ -54,7 +66,7 @@ Parse_Info (infofile, repository, callproc, all) /* search the info file for lines that match */ callback_done = line_number = 0; - while (fgets (line, sizeof (line), fp_info) != NULL) + while (getline (&line, &line_allocated, fp_info) >= 0) { line_number++; @@ -146,7 +158,10 @@ Parse_Info (infofile, repository, callproc, all) err += callproc (repository, expanded_value); callback_done = 1; } - (void) fclose (fp_info); + if (ferror (fp_info)) + error (0, errno, "cannot read %s", infopath); + if (fclose (fp_info) < 0) + error (0, errno, "cannot close %s", infopath); /* if we fell through and didn't callback at all, do the default */ if (callback_done == 0 && default_value != NULL) @@ -157,6 +172,9 @@ Parse_Info (infofile, repository, callproc, all) free (default_value); if (expanded_value != NULL) free (expanded_value); + free (infopath); + if (line != NULL) + free (line); return (err); } diff --git a/contrib/cvs/src/patch.c b/contrib/cvs/src/patch.c index 39b4e64..4c21943 100644 --- a/contrib/cvs/src/patch.c +++ b/contrib/cvs/src/patch.c @@ -16,8 +16,10 @@ #include "getline.h" static RETSIGTYPE patch_cleanup PROTO((void)); -static Dtype patch_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int patch_fileproc PROTO((struct file_info *finfo)); +static Dtype patch_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int patch_proc PROTO((int *pargc, char **argv, char *xwhere, char *mwhere, char *mfile, int shorten, int local_specified, char *mname, char *msg)); @@ -28,20 +30,23 @@ static int toptwo_diffs = 0; static int local = 0; static char *options = NULL; static char *rev1 = NULL; -static int rev1_validated = 1; +static int rev1_validated = 0; static char *rev2 = NULL; -static int rev2_validated = 1; +static int rev2_validated = 0; static char *date1 = NULL; static char *date2 = NULL; -static char tmpfile1[L_tmpnam+1], tmpfile2[L_tmpnam+1], tmpfile3[L_tmpnam+1]; +static char *tmpfile1 = NULL; +static char *tmpfile2 = NULL; +static char *tmpfile3 = NULL; static int unidiff = 0; static const char *const patch_usage[] = { - "Usage: %s %s [-fl] [-c|-u] [-s|-t] [-V %%d]\n", + "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d]\n", " -r rev|-D date [-r rev2 | -D date2] modules...\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", + "\t-R\tProcess directories recursively.\n", "\t-c\tContext diffs (default)\n", "\t-u\tUnidiff format.\n", "\t-s\tShort patch - one liner per file.\n", @@ -66,7 +71,7 @@ patch (argc, argv) usage (patch_usage); optind = 1; - while ((c = getopt (argc, argv, "V:k:cuftsQqlRD:r:")) != -1) + while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1) { switch (c) { @@ -120,12 +125,31 @@ patch (argc, argv) options = RCS_check_kflag (optarg); break; case 'V': + /* This option is pretty seriously broken: + 1. It is not clear what it does (does it change keyword + expansion behavior? If so, how? Or does it have + something to do with what version of RCS we are using? + Or the format we write RCS files in?). + 2. Because both it and -k use the options variable, + specifying both -V and -k doesn't work. + 3. At least as of CVS 1.9, it doesn't work (failed + assertion in RCS_checkout where it asserts that options + starts with -k). Few people seem to be complaining. + In the future (perhaps the near future), I have in mind + removing it entirely, and updating NEWS and cvs.texinfo, + but in case it is a good idea to give people more time + to complain if they would miss it, I'll just add this + quick and dirty error message for now. */ + error (1, 0, + "the -V option is obsolete and should not be used"); +#if 0 if (atoi (optarg) <= 0) error (1, 0, "must specify a version number to -V"); if (options) free (options); options = xmalloc (strlen (optarg) + 1 + 2); /* for the -V */ (void) sprintf (options, "-V%s", optarg); +#endif break; case 'u': unidiff = 1; /* Unidiff */ @@ -173,7 +197,7 @@ patch (argc, argv) if (local) send_arg("-l"); - if (force_tag_match) + if (!force_tag_match) send_arg("-f"); if (toptwo_diffs) send_arg("-t"); @@ -235,7 +259,6 @@ patch (argc, argv) * callback proc for doing the real work of patching */ /* ARGSUSED */ -static char where[PATH_MAX]; static int patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, mname, msg) @@ -251,16 +274,21 @@ patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, { int err = 0; int which; - char repository[PATH_MAX]; - - (void) sprintf (repository, "%s/%s", CVSroot, argv[0]); + char *repository; + char *where; + + repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile)) + 30); + (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); + where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile)) + + 10); (void) strcpy (where, argv[0]); /* if mfile isn't null, we need to set up to do only part of the module */ if (mfile != NULL) { char *cp; - char path[PATH_MAX]; + char *path; /* if the portion of the module is a path, put the dir part on repos */ if ((cp = strrchr (mfile, '/')) != NULL) @@ -274,6 +302,7 @@ patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, } /* take care of the rest */ + path = xmalloc (strlen (repository) + strlen (mfile) + 5); (void) sprintf (path, "%s/%s", repository, mfile); if (isdir (path)) { @@ -292,14 +321,17 @@ patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, argv[1] = xstrdup (mfile); (*pargc) = 2; } + free (path); } /* cd to the starting repository */ - if (chdir (repository) < 0) + if ( CVS_CHDIR (repository) < 0) { error (0, errno, "cannot chdir to %s", repository); + free (repository); return (1); } + free (repository); if (force_tag_match) which = W_REPOS | W_ATTIC; @@ -319,8 +351,10 @@ patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, /* start the recursion processor */ err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc, - (DIRLEAVEPROC) NULL, *pargc - 1, argv + 1, local, - which, 0, 1, where, 1, 1); + (DIRLEAVEPROC) NULL, NULL, + *pargc - 1, argv + 1, local, + which, 0, 1, where, 1); + free (where); return (err); } @@ -331,62 +365,99 @@ patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, */ /* ARGSUSED */ static int -patch_fileproc (finfo) +patch_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { struct utimbuf t; char *vers_tag, *vers_head; - char rcsspace[1][PATH_MAX]; - char *rcs = rcsspace[0]; + char *rcs = NULL; RCSNode *rcsfile; FILE *fp1, *fp2, *fp3; int ret = 0; int isattic = 0; int retcode = 0; - char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX]; + char *file1; + char *file2; + char *strippath; char *line1, *line2; size_t line1_chars_allocated; size_t line2_chars_allocated; char *cp1, *cp2; FILE *fp; + line1 = NULL; + line1_chars_allocated = 0; + line2 = NULL; + line2_chars_allocated = 0; + /* find the parsed rcs file */ if ((rcsfile = finfo->rcs) == NULL) - return (1); + { + ret = 1; + goto out2; + } if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC)) isattic = 1; + rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5); (void) sprintf (rcs, "%s%s", finfo->file, RCSEXT); /* if vers_head is NULL, may have been removed from the release */ if (isattic && rev2 == NULL && date2 == NULL) vers_head = NULL; else - vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match, 0); + { + vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match, + (int *) NULL); + if (vers_head != NULL && RCS_isdead (rcsfile, vers_head)) + { + free (vers_head); + vers_head = NULL; + } + } if (toptwo_diffs) { if (vers_head == NULL) - return (1); + { + ret = 1; + goto out2; + } if (!date1) - date1 = xmalloc (50); /* plenty big :-) */ + date1 = xmalloc (MAXDATELEN); *date1 = '\0'; if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1) { if (!really_quiet) error (0, 0, "cannot find date in rcs file %s revision %s", rcs, vers_head); - return (1); + ret = 1; + goto out2; } } - vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, 0); + vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, + (int *) NULL); + if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag)) + { + free (vers_tag); + vers_tag = NULL; + } - if (vers_tag == NULL && (vers_head == NULL || isattic)) - return (0); /* nothing known about specified revs */ + if (vers_tag == NULL && vers_head == NULL) + { + /* Nothing known about specified revs. */ + ret = 0; + goto out2; + } if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0) - return (0); /* not changed between releases */ + { + /* Not changed between releases. */ + ret = 0; + goto out2; + } if (patch_short) { @@ -407,24 +478,31 @@ patch_fileproc (finfo) else (void) printf ("changed from revision %s to %s\n", vers_tag, vers_head); - return (0); + ret = 0; + goto out2; } - if ((fp1 = fopen (tmpnam (tmpfile1), "w+")) != NULL) + tmpfile1 = cvs_temp_name (); + if ((fp1 = CVS_FOPEN (tmpfile1, "w+")) != NULL) (void) fclose (fp1); - if ((fp2 = fopen (tmpnam (tmpfile2), "w+")) != NULL) + tmpfile2 = cvs_temp_name (); + if ((fp2 = CVS_FOPEN (tmpfile2, "w+")) != NULL) (void) fclose (fp2); - if ((fp3 = fopen (tmpnam (tmpfile3), "w+")) != NULL) + tmpfile3 = cvs_temp_name (); + if ((fp3 = CVS_FOPEN (tmpfile3, "w+")) != NULL) (void) fclose (fp3); if (fp1 == NULL || fp2 == NULL || fp3 == NULL) { + /* FIXME: should be printing a proper error message, with errno-based + message, and the filename which we could not create. */ error (0, 0, "cannot create temporary files"); ret = 1; goto out; } if (vers_tag != NULL) { - retcode = RCS_checkout (rcsfile->path, NULL, vers_tag, options, tmpfile1, - 0, 0); + retcode = RCS_checkout (rcsfile, (char *) NULL, vers_tag, + rev1, options, tmpfile1, + (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { if (!really_quiet) @@ -445,7 +523,9 @@ patch_fileproc (finfo) } if (vers_head != NULL) { - retcode = RCS_checkout (rcsfile->path, NULL, vers_head, options, tmpfile2, 0, 0); + retcode = RCS_checkout (rcsfile, (char *) NULL, vers_head, + rev2, options, tmpfile2, + (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { if (!really_quiet) @@ -462,11 +542,6 @@ patch_fileproc (finfo) run_arg (tmpfile1); run_arg (tmpfile2); - line1 = NULL; - line1_chars_allocated = 0; - line2 = NULL; - line2_chars_allocated = 0; - switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_REALLY)) { case -1: /* fork/wait failure */ @@ -522,20 +597,30 @@ patch_fileproc (finfo) goto out; } } - if (CVSroot != NULL) - (void) sprintf (strippath, "%s/", CVSroot); + if (CVSroot_directory != NULL) + { + strippath = xmalloc (strlen (CVSroot_directory) + 10); + (void) sprintf (strippath, "%s/", CVSroot_directory); + } else - (void) strcpy (strippath, REPOS_STRIP); + strippath = xstrdup (REPOS_STRIP); if (strncmp (rcs, strippath, strlen (strippath)) == 0) rcs += strlen (strippath); + free (strippath); if (vers_tag != NULL) { + file1 = xmalloc (strlen (finfo->fullname) + + strlen (vers_tag) + + 10); (void) sprintf (file1, "%s:%s", finfo->fullname, vers_tag); } else { - (void) strcpy (file1, DEVNULL); + file1 = xstrdup (DEVNULL); } + file2 = xmalloc (strlen (finfo->fullname) + + (vers_head != NULL ? strlen (vers_head) : 10) + + 10); (void) sprintf (file2, "%s:%s", finfo->fullname, vers_head ? vers_head : "removed"); @@ -558,6 +643,8 @@ patch_fileproc (finfo) while (getline (&line1, &line1_chars_allocated, fp) >= 0) (void) fputs (line1, stdout); (void) fclose (fp); + free (file1); + free (file2); break; default: error (0, 0, "diff failed for %s", finfo->fullname); @@ -568,9 +655,17 @@ patch_fileproc (finfo) if (line2) free (line2); /* FIXME: should be checking for errors. */ - (void) unlink (tmpfile1); - (void) unlink (tmpfile2); - (void) unlink (tmpfile3); + (void) CVS_UNLINK (tmpfile1); + (void) CVS_UNLINK (tmpfile2); + (void) CVS_UNLINK (tmpfile3); + free (tmpfile1); + free (tmpfile2); + free (tmpfile3); + tmpfile1 = tmpfile2 = tmpfile3 = NULL; + + out2: + if (rcs != NULL) + free (rcs); return (ret); } @@ -579,10 +674,12 @@ patch_fileproc (finfo) */ /* ARGSUSED */ static Dtype -patch_dirproc (dir, repos, update_dir) +patch_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "Diffing %s", update_dir); @@ -595,10 +692,20 @@ patch_dirproc (dir, repos, update_dir) static RETSIGTYPE patch_cleanup () { - if (tmpfile1[0] != '\0') + if (tmpfile1 != NULL) + { (void) unlink_file (tmpfile1); - if (tmpfile2[0] != '\0') + free (tmpfile1); + } + if (tmpfile2 != NULL) + { (void) unlink_file (tmpfile2); - if (tmpfile3[0] != '\0') + free (tmpfile2); + } + if (tmpfile3 != NULL) + { (void) unlink_file (tmpfile3); + free (tmpfile3); + } + tmpfile1 = tmpfile2 = tmpfile3 = NULL; } diff --git a/contrib/cvs/src/rcs.h b/contrib/cvs/src/rcs.h index 698a3b1..3de8c91 100644 --- a/contrib/cvs/src/rcs.h +++ b/contrib/cvs/src/rcs.h @@ -1,5 +1,3 @@ -/* $CVSid: @(#)rcs.h 1.18 94/09/23 $ */ - /* * Copyright (c) 1992, Brian Berliner and Jeff Polk * Copyright (c) 1989-1992, Brian Berliner @@ -12,11 +10,12 @@ #define RCS "rcs" #define RCS_CI "ci" -#define RCS_CO "co" -#define RCS_RLOG "rlog" #define RCS_DIFF "rcsdiff" #define RCS_RCSMERGE "rcsmerge" -#define RCS_MERGE_PAT "^>>>>>>> " /* runs "grep" with this pattern */ + +/* String which indicates a conflict if it occurs at the start of a line. */ +#define RCS_MERGE_PAT ">>>>>>> " + #define RCSEXT ",v" #define RCSPAT "*,v" #define RCSHEAD "head" @@ -41,18 +40,27 @@ #define VALID 0x1 /* flags field contains valid data */ #define INATTIC 0x2 /* RCS file is located in the Attic */ #define PARTIAL 0x4 /* RCS file not completly parsed */ +#define NODELTA 0x8 /* delta_pos no longer valid */ struct rcsnode { int refcount; int flags; + + /* File name of the RCS file. This is not necessarily the name + as specified by the user, but it is a name which can be passed to + system calls and a name which is OK to print in error messages + (the various names might differ in case). */ char *path; + char *head; char *branch; char *symbols_data; char *expand; List *symbols; List *versions; + long delta_pos; + List *other; }; typedef struct rcsnode RCSNode; @@ -62,9 +70,11 @@ struct rcsversnode char *version; char *date; char *author; + char *state; char *next; int dead; List *branches; + List *other; }; typedef struct rcsversnode RCSVers; @@ -77,17 +87,21 @@ typedef struct rcsversnode RCSVers; */ #define RCS_MAGIC_BRANCH 0 +/* The type of a function passed to RCS_checkout. */ +typedef void (*RCSCHECKOUTPROC) PROTO ((void *, const char *, size_t)); + /* * exported interfaces */ RCSNode *RCS_parse PROTO((const char *file, const char *repos)); RCSNode *RCS_parsercsfile PROTO((char *rcsfile)); +void RCS_fully_parse PROTO((RCSNode *)); char *RCS_check_kflag PROTO((const char *arg)); char *RCS_getdate PROTO((RCSNode * rcs, char *date, int force_tag_match)); char *RCS_gettag PROTO((RCSNode * rcs, char *symtag, int force_tag_match, - int return_both)); + int *simple_tag)); char *RCS_getversion PROTO((RCSNode * rcs, char *tag, char *date, - int force_tag_match, int return_both)); + int force_tag_match, int *simple_tag)); char *RCS_magicrev PROTO((RCSNode *rcs, char *rev)); int RCS_isbranch PROTO((RCSNode *rcs, const char *rev)); int RCS_nodeisbranch PROTO((RCSNode *rcs, const char *tag)); @@ -102,3 +116,13 @@ char *RCS_getbranch PROTO((RCSNode * rcs, char *tag, int force_tag_match)); int RCS_isdead PROTO((RCSNode *, const char *)); char *RCS_getexpand PROTO ((RCSNode *)); +int RCS_checkout PROTO ((RCSNode *, char *, char *, char *, char *, char *, + RCSCHECKOUTPROC, void *)); +int RCS_cmp_file PROTO ((RCSNode *, char *, char *, const char *)); +int RCS_settag PROTO ((RCSNode *, const char *, const char *)); +int RCS_deltag PROTO ((RCSNode *, const char *, int)); +int RCS_setbranch PROTO((RCSNode *, const char *)); +int RCS_lock PROTO ((RCSNode *, const char *, int)); +int RCS_unlock PROTO ((RCSNode *, const char *, int)); +int rcs_change_text PROTO ((const char *, char *, size_t, const char *, + size_t, char **, size_t *)); diff --git a/contrib/cvs/src/rcscmds.c b/contrib/cvs/src/rcscmds.c index 66aea57..1f93fcf 100644 --- a/contrib/cvs/src/rcscmds.c +++ b/contrib/cvs/src/rcscmds.c @@ -12,28 +12,73 @@ #include "cvs.h" #include <assert.h> +/* This file, rcs.h, and rcs.c, are intended to define our interface + to RCS files. As of July, 1996, there are still a few places that + still exec RCS commands directly. The intended long-term direction + is to have CVS access RCS files via an RCS library (rcs.c can be + considered a start at one), for performance, cleanliness (CVS has + some awful hacks to work around RCS behaviors which don't make + sense for CVS), installation hassles, ease of implementing the CVS + server (I don't think that the output-out-of-order bug can be + completely fixed as long as CVS calls RCS), and perhaps other + reasons. + + Whether there will also be a version of RCS which uses this + library, or whether the library will be packaged for uses beyond + CVS or RCS (many people would like such a thing) is an open + question. Some considerations: + + 1. An RCS library for CVS must have the capabilities of the + existing CVS code which accesses RCS files. In particular, simple + approaches will often be slow. + + 2. An RCS library should not use the code from the current RCS + (5.7 and its ancestors). The code has many problems. Too few + comments, too many layers of abstraction, too many global variables + (the correct number for a library is zero), too much intricately + interwoven functionality, and too many clever hacks. Paul Eggert, + the current RCS maintainer, agrees. + + 3. More work needs to be done in terms of separating out the RCS + library from the rest of CVS (for example, cvs_output should be + replaced by a callback, and the declarations should be centralized + into rcs.h, and probably other such cleanups). + + 4. To be useful for RCS and perhaps for other uses, the library + may need features beyond those needed by CVS. + + 5. Any changes to the RCS file format *must* be compatible. Many, + many tools (not just CVS and RCS) can at least import this format. + RCS and CVS must preserve the current ability to import/export it + (preferably improved--magic branches are currently a roadblock). + See doc/RCSFILES in the CVS distribution for documentation of this + file format. + + On somewhat related notes: + + 1. A library for diff is an obvious idea. The one thing which I'm + not so sure about is that I think CVS probably wants the ability to + allow arbitrarily-bizarre (and possibly customized for particular + file formats) external diff programs. + + 2. A library for patch is another such idea. CVS's needs are + smaller than the functionality of the standalone patch program (it + only calls patch in the client, and only needs to be able to patch + unmodified versions, which is something that RCS_deltas already + does in a different context). But it is silly for CVS to be making + people install patch as well as CVS for such a simple purpose. */ + /* For RCS file PATH, make symbolic tag TAG point to revision REV. This validates that TAG is OK for a user to use. Return value is -1 for error (and errno is set to indicate the error), positive for error (and an error message has been printed), or zero for success. */ int -RCS_settag(path, tag, rev) +RCS_exec_settag(path, tag, rev) const char *path; const char *tag; const char *rev; { - if (strcmp (tag, TAG_BASE) == 0 - || strcmp (tag, TAG_HEAD) == 0) - { - /* Print the name of the tag might be considered redundant - with the caller, which also prints it. Perhaps this helps - clarify why the tag name is considered reserved, I don't - know. */ - error (0, 0, "Attempt to add reserved tag name %s", tag); - return 1; - } - run_setup ("%s%s -x,v/ -q -N%s:%s", Rcsbin, RCS, tag, rev); run_arg (path); return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); @@ -42,7 +87,7 @@ RCS_settag(path, tag, rev) /* NOERR is 1 to suppress errors--FIXME it would be better to avoid the errors or some cleaner solution. */ int -RCS_deltag(path, tag, noerr) +RCS_exec_deltag(path, tag, noerr) const char *path; const char *tag; int noerr; @@ -54,7 +99,7 @@ RCS_deltag(path, tag, noerr) /* set RCS branch to REV */ int -RCS_setbranch(path, rev) +RCS_exec_setbranch(path, rev) const char *path; const char *rev; { @@ -66,7 +111,7 @@ RCS_setbranch(path, rev) /* Lock revision REV. NOERR is 1 to suppress errors--FIXME it would be better to avoid the errors or some cleaner solution. */ int -RCS_lock(path, rev, noerr) +RCS_exec_lock(path, rev, noerr) const char *path; const char *rev; int noerr; @@ -79,7 +124,7 @@ RCS_lock(path, rev, noerr) /* Unlock revision REV. NOERR is 1 to suppress errors--FIXME it would be better to avoid the errors or some cleaner solution. */ int -RCS_unlock(path, rev, noerr) +RCS_exec_unlock(path, rev, noerr) const char *path; const char *rev; int noerr; @@ -107,71 +152,53 @@ RCS_merge(path, options, rev1, rev2) #ifndef HAVE_RCS5 if (status == 0) { - /* Run GREP to see if there appear to be conflicts in the file */ - run_setup ("%s", GREP); - run_arg (RCS_MERGE_PAT); - run_arg (path); - status = (run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_NORMAL) == 0); - + error (1, 0, "CVS no longer supports RCS versions older than RCS5"); + /* This case needs to call file_has_markers to see if the file + contains conflict indicators. But is anyone using the !HAVE_RCS5 + code any more? */ } #endif return status; } -/* Check out a revision from RCSFILE into WORKFILE, or to standard output - if WORKFILE is NULL. If WORKFILE is "", let RCS pick the working file - name. TAG is the tag to check out, or NULL if one should check out - the head of the default branch. OPTIONS is a string such as - -kb or -kkv, for keyword expansion options, or NULL if there are none. - If WORKFILE is NULL, run regardless of noexec; if non-NULL, noexec - inhibits execution. SOUT is what to do with standard output - (typically RUN_TTY). If FLAGS & RCS_FLAGS_LOCK, lock it. If - FLAGS & RCS_FLAGS_FORCE, check out even on top of an existing file. - If NOERR is nonzero, suppress errors. */ -int -RCS_checkout (rcsfile, workfile, tag, options, sout, flags, noerr) - char *rcsfile; - char *workfile; - char *tag; - char *options; - char *sout; - int flags; - int noerr; -{ - run_setup ("%s%s -x,v/ -q %s%s", Rcsbin, RCS_CO, - tag ? "-r" : "", tag ? tag : ""); - if (options != NULL && options[0] != '\0') - run_arg (options); - if (workfile == NULL) - run_arg ("-p"); - if (flags & RCS_FLAGS_LOCK) - run_arg ("-l"); - if (flags & RCS_FLAGS_FORCE) - run_arg ("-f"); - run_arg (rcsfile); - if (workfile != NULL && workfile[0] != '\0') - run_arg (workfile); - return run_exec (RUN_TTY, sout, noerr ? DEVNULL : RUN_TTY, - workfile == NULL ? (RUN_NORMAL | RUN_REALLY) : RUN_NORMAL); -} - /* Check in to RCSFILE with revision REV (which must be greater than the largest revision) and message MESSAGE (which is checked for legality). - If FLAGS & RCS_FLAGS_DEAD, check in a dead revision. If NOERR, do not - report errors. If FLAGS & RCS_FLAGS_QUIET suppress errors somewhat more - selectively. If FLAGS & RCS_FLAGS_MODTIME, use the working file's - modification time for the checkin time. WORKFILE is the working file - to check in from, or NULL to use the usual RCS rules for deriving it - from the RCSFILE. */ + If FLAGS & RCS_FLAGS_DEAD, check in a dead revision. If FLAGS & + RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS & RCS_FLAGS_MODTIME, + use the working file's modification time for the checkin time. + WORKFILE is the working file to check in from, or NULL to use the usual + RCS rules for deriving it from the RCSFILE. + + Return value is -1 for error (and errno is set to indicate the + error), positive for error (and an error message has been printed), + or zero for success. */ int -RCS_checkin (rcsfile, workfile, message, rev, flags, noerr) +RCS_checkin (rcsfile, workfile, message, rev, flags) char *rcsfile; char *workfile; char *message; char *rev; int flags; - int noerr; { + /* The desired behavior regarding permissions is to preserve the + permissions on RCSFILE if it already exists. Based on looking + at the RCS 5.7 source, it would appear that RCS_CI does this + except when it is creating RCSFILE (reasonable), or when + RCSFILE was created with rcs -i (this is strange, and quite + possibly unintentional). In those two cases it copies the + permissions from the workfile. + + Anyway, the fix is simple enough: we preserve the mode ourself. */ + struct stat sb; + int fix_mode = 1; + int retval; + + if (CVS_STAT (rcsfile, &sb) < 0) + { + fix_mode = 0; + if (!existence_error (errno)) + error (0, errno, "warning: cannot stat %s", rcsfile); + } run_setup ("%s%s -x,v/ -f %s%s", Rcsbin, RCS_CI, rev ? "-r" : "", rev ? rev : ""); if (flags & RCS_FLAGS_DEAD) @@ -184,5 +211,12 @@ RCS_checkin (rcsfile, workfile, message, rev, flags, noerr) if (workfile != NULL) run_arg (workfile); run_arg (rcsfile); - return run_exec (RUN_TTY, RUN_TTY, noerr ? DEVNULL : RUN_TTY, RUN_NORMAL); + retval = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + if (retval == 0 && fix_mode) + { + if (chmod (rcsfile, sb.st_mode) < 0) + error (0, errno, "warning: cannot change permissions on %s", + rcsfile); + } + return retval; } diff --git a/contrib/cvs/src/recurse.c b/contrib/cvs/src/recurse.c index ec51a98..e18d576 100644 --- a/contrib/cvs/src/recurse.c +++ b/contrib/cvs/src/recurse.c @@ -19,79 +19,113 @@ static void addlist PROTO((List ** listp, char *key)); static int unroll_files_proc PROTO((Node *p, void *closure)); static void addfile PROTO((List **listp, char *dir, char *file)); - -/* - * Local static versions eliminates the need for globals - */ -static FILEPROC fileproc; -static FILESDONEPROC filesdoneproc; -static DIRENTPROC direntproc; -static DIRLEAVEPROC dirleaveproc; -static int which; -static Dtype flags; -static int aflag; -static int readlock; -static int dosrcs; -static char update_dir[PATH_MAX]; +static char *update_dir; static char *repository = NULL; static List *filelist = NULL; /* holds list of files on which to operate */ static List *dirlist = NULL; /* holds list of directories on which to operate */ struct recursion_frame { - FILEPROC fileproc; - FILESDONEPROC filesdoneproc; - DIRENTPROC direntproc; - DIRLEAVEPROC dirleaveproc; - Dtype flags; - int which; - int aflag; - int readlock; - int dosrcs; + FILEPROC fileproc; + FILESDONEPROC filesdoneproc; + DIRENTPROC direntproc; + DIRLEAVEPROC dirleaveproc; + void *callerdat; + Dtype flags; + int which; + int aflag; + int readlock; + int dosrcs; }; -/* - * Called to start a recursive command. - * - * Command line arguments dictate the directories and files on which - * we operate. In the special case of no arguments, we default to - * ".". - * - * The general algorithm is as follows. - */ +static int do_recursion PROTO ((struct recursion_frame *frame)); + +/* I am half tempted to shove a struct file_info * into the struct + recursion_frame (but then we would need to modify or create a + recursion_frame for each file), or shove a struct recursion_frame * + into the struct file_info (more tempting, although it isn't completely + clear that the struct file_info should contain info about recursion + processor internals). So instead use this struct. */ + +struct frame_and_file { + struct recursion_frame *frame; + struct file_info *finfo; +}; + +/* Similarly, we need to pass the entries list to do_dir_proc. */ + +struct frame_and_entries { + struct recursion_frame *frame; + List *entries; +}; + +/* Start a recursive command. + + Command line arguments (ARGC, ARGV) dictate the directories and + files on which we operate. In the special case of no arguments, we + default to ".". */ int -start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, +start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, argc, argv, local, which, aflag, readlock, - update_preload, dosrcs, wd_is_repos) + update_preload, dosrcs) FILEPROC fileproc; FILESDONEPROC filesdoneproc; DIRENTPROC direntproc; DIRLEAVEPROC dirleaveproc; + void *callerdat; + int argc; char **argv; int local; + + /* This specifies the kind of recursion. There are several cases: + + 1. W_LOCAL is not set but W_REPOS or W_ATTIC is. The current + directory when we are called must be the repository and + recursion proceeds according to what exists in the repository. + + 2a. W_LOCAL is set but W_REPOS and W_ATTIC are not. The + current directory when we are called must be the working + directory. Recursion proceeds according to what exists in the + working directory, never (I think) consulting any part of the + repository which does not correspond to the working directory + ("correspond" == Name_Repository). + + 2b. W_LOCAL is set and so is W_REPOS or W_ATTIC. This is the + weird one. The current directory when we are called must be + the working directory. We recurse through working directories, + but we recurse into a directory if it is exists in the working + directory *or* it exists in the repository. If a directory + does not exist in the working directory, the direntproc must + either tell us to skip it (R_SKIP_ALL), or must create it (I + think those are the only two cases). */ int which; + int aflag; int readlock; char *update_preload; int dosrcs; - int wd_is_repos; /* Set if caller has already cd'd to the repository */ { int i, err = 0; - Dtype flags; List *files_by_dir = NULL; struct recursion_frame frame; + frame.fileproc = fileproc; + frame.filesdoneproc = filesdoneproc; + frame.direntproc = direntproc; + frame.dirleaveproc = dirleaveproc; + frame.callerdat = callerdat; + frame.flags = local ? R_SKIP_DIRS : R_PROCESS; + frame.which = which; + frame.aflag = aflag; + frame.readlock = readlock; + frame.dosrcs = dosrcs; + expand_wild (argc, argv, &argc, &argv); if (update_preload == NULL) - update_dir[0] = '\0'; - else - (void) strcpy (update_dir, update_preload); - - if (local) - flags = R_SKIP_DIRS; + update_dir = xstrdup (""); else - flags = R_PROCESS; + update_dir = xstrdup (update_preload); /* clean up from any previous calls to start_recursion */ if (repository) @@ -101,10 +135,17 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, } if (filelist) dellist (&filelist); /* FIXME-krp: no longer correct. */ -/* FIXME-krp: clean up files_by_dir */ if (dirlist) dellist (&dirlist); +#ifdef SERVER_SUPPORT + if (server_active) + { + for (i = 0; i < argc; ++i) + server_pathname_check (argv[i]); + } +#endif + if (argc == 0) { @@ -116,14 +157,12 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, * called with the list of sub-dirs of the current dir as args */ if ((which & W_LOCAL) && !isdir (CVSADM)) - dirlist = Find_Directories ((char *) NULL, W_LOCAL); + dirlist = Find_Directories ((char *) NULL, W_LOCAL, (List *) NULL); else addlist (&dirlist, "."); - err += do_recursion (fileproc, filesdoneproc, direntproc, - dirleaveproc, flags, which, aflag, - readlock, dosrcs); - return(err); + err += do_recursion (&frame); + goto out; } @@ -151,7 +190,6 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, /* otherwise, split argument into directory and component names. */ char *dir; char *comp; - char tmp[PATH_MAX]; char *file_to_try; /* Now break out argv[i] into directory part (DIR) and file part (COMP). @@ -174,45 +212,55 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, /* if this argument exists as a file in the current working directory tree, then add it to the files list. */ - if (wd_is_repos) + if (!(which & W_LOCAL)) { /* If doing rtag, we've done a chdir to the repository. */ - sprintf (tmp, "%s%s", argv[i], RCSEXT); - file_to_try = tmp; + file_to_try = xmalloc (strlen (argv[i]) + sizeof (RCSEXT) + 5); + sprintf (file_to_try, "%s%s", argv[i], RCSEXT); } else - file_to_try = argv[i]; + file_to_try = xstrdup (argv[i]); - if(isfile(file_to_try)) + if (isfile (file_to_try)) addfile (&files_by_dir, dir, comp); else if (isdir (dir)) { - if (isdir (CVSADM)) + if ((which & W_LOCAL) && isdir (CVSADM) +#ifdef CLIENT_SUPPORT + && !client_active +#endif + ) { /* otherwise, look for it in the repository. */ - char *save_update_dir; + char *tmp_update_dir; char *repos; - - /* save & set (aka push) update_dir */ - save_update_dir = xstrdup (update_dir); + char *reposfile; + + tmp_update_dir = xmalloc (strlen (update_dir) + + strlen (dir) + + 5); + strcpy (tmp_update_dir, update_dir); - if (*update_dir != '\0') - (void) strcat (update_dir, "/"); + if (*tmp_update_dir != '\0') + (void) strcat (tmp_update_dir, "/"); + + (void) strcat (tmp_update_dir, dir); - (void) strcat (update_dir, dir); - /* look for it in the repository. */ - repos = Name_Repository (dir, update_dir); - (void) sprintf (tmp, "%s/%s", repos, comp); + repos = Name_Repository (dir, tmp_update_dir); + reposfile = xmalloc (strlen (repos) + + strlen (comp) + + 5); + (void) sprintf (reposfile, "%s/%s", repos, comp); free (repos); - if (!wrap_name_has (comp, WRAP_TOCVS) && isdir(tmp)) + if (!wrap_name_has (comp, WRAP_TOCVS) && isdir (reposfile)) addlist (&dirlist, argv[i]); else addfile (&files_by_dir, dir, comp); - (void) sprintf (update_dir, "%s", save_update_dir); - free (save_update_dir); + free (tmp_update_dir); + free (reposfile); } else addfile (&files_by_dir, dir, comp); @@ -220,6 +268,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, else error (1, 0, "no such directory `%s'", dir); + free (file_to_try); free (dir); free (comp); } @@ -229,29 +278,21 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, a coupla lists. Now we unroll the lists, setting up and calling do_recursion. */ - frame.fileproc = fileproc; - frame.filesdoneproc = filesdoneproc; - frame.direntproc = direntproc; - frame.dirleaveproc = dirleaveproc; - frame.flags = flags; - frame.which = which; - frame.aflag = aflag; - frame.readlock = readlock; - frame.dosrcs = dosrcs; err += walklist (files_by_dir, unroll_files_proc, (void *) &frame); + dellist(&files_by_dir); /* then do_recursion on the dirlist. */ if (dirlist != NULL) - err += do_recursion (frame.fileproc, frame.filesdoneproc, - frame.direntproc, frame.dirleaveproc, - frame.flags, frame.which, frame.aflag, - frame.readlock, frame.dosrcs); + err += do_recursion (&frame); /* Free the data which expand_wild allocated. */ for (i = 0; i < argc; ++i) free (argv[i]); free (argv); + out: + free (update_dir); + update_dir = NULL; return (err); } @@ -259,38 +300,21 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, * Implement the recursive policies on the local directory. This may be * called directly, or may be called by start_recursion */ -int -do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, - xflags, xwhich, xaflag, xreadlock, xdosrcs) - FILEPROC xfileproc; - FILESDONEPROC xfilesdoneproc; - DIRENTPROC xdirentproc; - DIRLEAVEPROC xdirleaveproc; - Dtype xflags; - int xwhich; - int xaflag; - int xreadlock; - int xdosrcs; +static int +do_recursion (frame) + struct recursion_frame *frame; { int err = 0; int dodoneproc = 1; char *srepository; List *entries = NULL; + int should_readlock; /* do nothing if told */ - if (xflags == R_SKIP_ALL) + if (frame->flags == R_SKIP_ALL) return (0); - /* set up the static vars */ - fileproc = xfileproc; - filesdoneproc = xfilesdoneproc; - direntproc = xdirentproc; - dirleaveproc = xdirleaveproc; - flags = xflags; - which = xwhich; - aflag = xaflag; - readlock = noexec ? 0 : xreadlock; - dosrcs = xdosrcs; + should_readlock = noexec ? 0 : frame->readlock; /* The fact that locks are not active here is what makes us fail to have the @@ -332,14 +356,14 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, */ if (server_active /* If there are writelocks around, we cannot pause here. */ - && (readlock || noexec)) + && (should_readlock || noexec)) server_pause_check(); #endif /* * Fill in repository with the current repository */ - if (which & W_LOCAL) + if (frame->which & W_LOCAL) { if (isdir (CVSADM)) repository = Name_Repository ((char *) NULL, update_dir); @@ -348,8 +372,9 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, } else { - repository = xmalloc (PATH_MAX); - (void) getwd (repository); + repository = xgetwd (); + if (repository == NULL) + error (1, errno, "could not get working directory"); } srepository = repository; /* remember what to free */ @@ -373,41 +398,52 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, if (filelist == NULL && dirlist == NULL) { /* both lists were NULL, so start from scratch */ - if (fileproc != NULL && flags != R_SKIP_FILES) + if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) { - int lwhich = which; + int lwhich = frame->which; /* be sure to look in the attic if we have sticky tags/date */ if ((lwhich & W_ATTIC) == 0) if (isreadable (CVSADM_TAG)) lwhich |= W_ATTIC; + /* In the !(which & W_LOCAL) case, we filled in repository + earlier in the function. In the (which & W_LOCAL) case, + the Find_Names function is going to look through the + Entries file. If we do not have a repository, that + does not make sense, so we insist upon having a + repository at this point. Name_Repository will give a + reasonable error message. */ + if (repository == NULL) + repository = Name_Repository ((char *) NULL, update_dir); + /* find the files and fill in entries if appropriate */ - filelist = Find_Names (repository, lwhich, aflag, &entries); + filelist = Find_Names (repository, lwhich, frame->aflag, &entries); } /* find sub-directories if we will recurse */ - if (flags != R_SKIP_DIRS) - dirlist = Find_Directories (repository, which); + if (frame->flags != R_SKIP_DIRS) + dirlist = Find_Directories (repository, frame->which, entries); } else { /* something was passed on the command line */ - if (filelist != NULL && fileproc != NULL) + if (filelist != NULL && frame->fileproc != NULL) { /* we will process files, so pre-parse entries */ - if (which & W_LOCAL) - entries = Entries_Open (aflag); + if (frame->which & W_LOCAL) + entries = Entries_Open (frame->aflag); } } /* process the files (if any) */ - if (filelist != NULL && fileproc) + if (filelist != NULL && frame->fileproc) { struct file_info finfo_struct; + struct frame_and_file frfile; /* read lock it if necessary */ - if (readlock && repository && Reader_Lock (repository) != 0) + if (should_readlock && repository && Reader_Lock (repository) != 0) error (1, 0, "read lock failed - giving up"); #ifdef CLIENT_SUPPORT @@ -424,39 +460,50 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, finfo_struct.entries = entries; /* do_file_proc will fill in finfo_struct.file. */ + frfile.finfo = &finfo_struct; + frfile.frame = frame; + /* process the files */ - err += walklist (filelist, do_file_proc, &finfo_struct); + err += walklist (filelist, do_file_proc, &frfile); /* unlock it */ - if (readlock) + if (should_readlock) Lock_Cleanup (); /* clean up */ dellist (&filelist); } - if (entries) - { - Entries_Close (entries); - entries = NULL; - } - /* call-back files done proc (if any) */ - if (dodoneproc && filesdoneproc != NULL) - err = filesdoneproc (err, repository, update_dir[0] ? update_dir : "."); + if (dodoneproc && frame->filesdoneproc != NULL) + err = frame->filesdoneproc (frame->callerdat, err, repository, + update_dir[0] ? update_dir : ".", + entries); fileattr_write (); fileattr_free (); /* process the directories (if necessary) */ if (dirlist != NULL) - err += walklist (dirlist, do_dir_proc, NULL); -#ifdef notdef - else if (dirleaveproc != NULL) - err += dirleaveproc(".", err, "."); + { + struct frame_and_entries frent; + + frent.frame = frame; + frent.entries = entries; + err += walklist (dirlist, do_dir_proc, (void *) &frent); + } +#if 0 + else if (frame->dirleaveproc != NULL) + err += frame->dirleaveproc (frame->callerdat, ".", err, "."); #endif dellist (&dirlist); + if (entries) + { + Entries_Close (entries); + entries = NULL; + } + /* free the saved copy of the pointer if necessary */ if (srepository) { @@ -475,7 +522,8 @@ do_file_proc (p, closure) Node *p; void *closure; { - struct file_info *finfo = (struct file_info *)closure; + struct frame_and_file *frfile = (struct frame_and_file *)closure; + struct file_info *finfo = frfile->finfo; int ret; finfo->file = p->key; @@ -490,15 +538,20 @@ do_file_proc (p, closure) } strcat (finfo->fullname, finfo->file); - if (dosrcs && repository) + if (frfile->frame->dosrcs && repository) finfo->rcs = RCS_parse (finfo->file, repository); else finfo->rcs = (RCSNode *) NULL; - ret = fileproc (finfo); + ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); freercsnode(&finfo->rcs); free (finfo->fullname); + /* Allow the user to monitor progress with tail -f. Doing this once + per file should be no big deal, but we don't want the performance + hit of flushing on every line like previous versions of CVS. */ + cvs_flushout (); + return (ret); } @@ -510,8 +563,11 @@ do_dir_proc (p, closure) Node *p; void *closure; { + struct frame_and_entries *frent = (struct frame_and_entries *) closure; + struct recursion_frame *frame = frent->frame; + struct recursion_frame xframe; char *dir = p->key; - char newrepos[PATH_MAX]; + char *newrepos; List *sdirlist; char *srepository; char *cp; @@ -519,6 +575,46 @@ do_dir_proc (p, closure) int stripped_dot = 0; int err = 0; struct saved_cwd cwd; + char *saved_update_dir; + + if (fncmp (dir, CVSADM) == 0) + { + /* This seems to most often happen when users (beginning users, + generally), try "cvs ci *" or something similar. On that + theory, it is possible that we should just silently skip the + CVSADM directories, but on the other hand, using a wildcard + like this isn't necessarily a practice to encourage (it operates + only on files which exist in the working directory, unlike + regular CVS recursion). */ + + /* FIXME-reentrancy: printed_cvs_msg should be in a "command + struct" or some such, so that it gets cleared for each new + command (this is possible using the remote protocol and a + custom-written client). The struct recursion_frame is not + far back enough though, some commands (commit at least) + will call start_recursion several times. An alternate solution + would be to take this whole check and move it to a new function + validate_arguments or some such that all the commands call + and which snips the offending directory from the argc,argv + vector. */ + static int printed_cvs_msg = 0; + if (!printed_cvs_msg) + { + error (0, 0, "warning: directory %s specified in argument", + dir); + error (0, 0, "\ +but CVS uses %s for its own purposes; skipping %s directory", + CVSADM, dir); + printed_cvs_msg = 1; + } + return 0; + } + + saved_update_dir = update_dir; + update_dir = xmalloc (strlen (saved_update_dir) + + strlen (dir) + + 5); + strcpy (update_dir, saved_update_dir); /* set up update_dir - skip dots if not at start */ if (strcmp (dir, ".") != 0) @@ -539,9 +635,12 @@ do_dir_proc (p, closure) * update -d and in that case the generated name will be correct. */ if (repository == NULL) - newrepos[0] = '\0'; + newrepos = xstrdup (""); else + { + newrepos = xmalloc (strlen (repository) + strlen (dir) + 5); (void) sprintf (newrepos, "%s/%s", repository, dir); + } } else { @@ -549,31 +648,33 @@ do_dir_proc (p, closure) (void) strcpy (update_dir, dir); if (repository == NULL) - newrepos[0] = '\0'; + newrepos = xstrdup (""); else - (void) strcpy (newrepos, repository); + newrepos = xstrdup (repository); } /* call-back dir entry proc (if any) */ - if (direntproc != NULL) - dir_return = direntproc (dir, newrepos, update_dir); + if (frame->direntproc != NULL) + dir_return = frame->direntproc (frame->callerdat, dir, newrepos, + update_dir, frent->entries); + free (newrepos); /* only process the dir if the return code was 0 */ if (dir_return != R_SKIP_ALL) { /* save our current directory and static vars */ if (save_cwd (&cwd)) - exit (EXIT_FAILURE); + error_exit (); sdirlist = dirlist; srepository = repository; dirlist = NULL; /* cd to the sub-directory */ - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) error (1, errno, "could not chdir to %s", dir); /* honor the global SKIP_DIRS (a.k.a. local) */ - if (flags == R_SKIP_DIRS) + if (frame->flags == R_SKIP_DIRS) dir_return = R_SKIP_DIRS; /* remember if the `.' will be stripped for subsequent dirs */ @@ -584,31 +685,37 @@ do_dir_proc (p, closure) } /* make the recursive call */ - err += do_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, - dir_return, which, aflag, readlock, dosrcs); + xframe = *frame; + xframe.flags = dir_return; + err += do_recursion (&xframe); /* put the `.' back if necessary */ if (stripped_dot) (void) strcpy (update_dir, "."); /* call-back dir leave proc (if any) */ - if (dirleaveproc != NULL) - err = dirleaveproc (dir, err, update_dir); + if (frame->dirleaveproc != NULL) + err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir, + frent->entries); /* get back to where we started and restore state vars */ if (restore_cwd (&cwd, NULL)) - exit (EXIT_FAILURE); + error_exit (); free_cwd (&cwd); dirlist = sdirlist; repository = srepository; } - /* put back update_dir */ + /* Put back update_dir. I think this is the same as just setting + update_dir back to saved_update_dir, but there are a few cases I'm + not sure about (in particular, if DIR is "." and update_dir is + not ""), so for conservatism I'm leaving this here. */ cp = last_component (update_dir); if (cp > update_dir) cp[-1] = '\0'; else update_dir[0] = '\0'; + free (saved_update_dir); return (err); } @@ -675,17 +782,22 @@ unroll_files_proc (p, closure) /* otherwise, call dorecusion for this list of files. */ filelist = (List *) p->data; + p->data = NULL; save_dirlist = dirlist; dirlist = NULL; if (strcmp(p->key, ".") != 0) { if (save_cwd (&cwd)) - exit (EXIT_FAILURE); - if (chdir (p->key) < 0) + error_exit (); + if ( CVS_CHDIR (p->key) < 0) error (1, errno, "could not chdir to %s", p->key); - save_update_dir = xstrdup (update_dir); + save_update_dir = update_dir; + update_dir = xmalloc (strlen (save_update_dir) + + strlen (p->key) + + 5); + strcpy (update_dir, save_update_dir); if (*update_dir != '\0') (void) strcat (update_dir, "/"); @@ -693,18 +805,15 @@ unroll_files_proc (p, closure) (void) strcat (update_dir, p->key); } - err += do_recursion (frame->fileproc, frame->filesdoneproc, - frame->direntproc, frame->dirleaveproc, - frame->flags, frame->which, frame->aflag, - frame->readlock, frame->dosrcs); + err += do_recursion (frame); if (save_update_dir != NULL) { - (void) strcpy (update_dir, save_update_dir); - free (save_update_dir); + free (update_dir); + update_dir = save_update_dir; if (restore_cwd (&cwd, NULL)) - exit (EXIT_FAILURE); + error_exit (); free_cwd (&cwd); } diff --git a/contrib/cvs/src/release.c b/contrib/cvs/src/release.c index b3ebb2b..f2f1edf 100644 --- a/contrib/cvs/src/release.c +++ b/contrib/cvs/src/release.c @@ -1,41 +1,61 @@ /* * Release: "cancel" a checkout in the history log. * - * - Don't allow release if anything is active - Don't allow release if not - * above or inside repository. - Don't allow release if ./CVS/Repository is - * not the same as the directory specified in the module database. - * * - Enter a line in the history log indicating the "release". - If asked to, * delete the local working directory. */ #include "cvs.h" +#include "getline.h" static void release_delete PROTO((char *dir)); static const char *const release_usage[] = { - "Usage: %s %s [-d] modules...\n", + "Usage: %s %s [-d] directories...\n", "\t-d\tDelete the given directory.\n", NULL }; -static short delete_flag; +#ifdef SERVER_SUPPORT +static int release_server PROTO ((int argc, char **argv)); + +/* This is the server side of cvs release. */ +static int +release_server (argc, argv) + int argc; + char **argv; +{ + int i; + + /* Note that we skip argv[0]. */ + for (i = 1; i < argc; ++i) + history_write ('F', argv[i], "", argv[i], ""); + return 0; +} + +#endif /* SERVER_SUPPORT */ -/* FIXME: This implementation is cheezy in quite a few ways: +/* There are various things to improve about this implementation: - 1. The whole "cvs update" junk could be checked locally with a + 1. Using run_popen to run "cvs update" could be replaced by a fairly simple start_recursion/classify_file loop--a win for - portability, performance, and cleanliness. + portability, performance, and cleanliness. In particular, there is + no particularly good way to find the right "cvs". - 2. Should be like edit/unedit in terms of working well if disconnected - from the network, and then sending a delayed notification. + 2. The fact that "cvs update" contacts the server slows things down; + it undermines the case for using "cvs release" rather than "rm -rf". + However, for correctly printing "? foo" and correctly handling + CVSROOTADM_IGNORE, we currently need to contact the server. - 3. Way too many network turnarounds. More than one for each argument. - Puh-leeze. + 3. Would be nice to take processing things on the client side one step + further, and making it like edit/unedit in terms of working well if + disconnected from the network, and then sending a delayed + notification. - 4. Oh, and as a purely stylistic nit, break this out into separate - functions for client/local and for server. Those #ifdefs are a mess. */ + 4. Having separate network turnarounds for the "Notify" request + which we do as part of unedit, and for the "release" itself, is slow + and unnecessary. */ int release (argc, argv) @@ -43,171 +63,140 @@ release (argc, argv) char **argv; { FILE *fp; - register int i, c; - char *repository, *srepos; - char line[PATH_MAX], update_cmd[PATH_MAX]; + int i, c; + char *repository; + char *line = NULL; + size_t line_allocated = 0; + char *update_cmd; char *thisarg; int arg_start_idx; int err = 0; + short delete_flag = 0; #ifdef SERVER_SUPPORT - if (!server_active) - { -#endif /* SERVER_SUPPORT */ - if (argc == -1) - usage (release_usage); - optind = 1; - while ((c = getopt (argc, argv, "Qdq")) != -1) - { - switch (c) - { - case 'Q': - case 'q': -#ifdef SERVER_SUPPORT - /* The CVS 1.5 client sends these options (in addition to - Global_option requests), so we must ignore them. */ - if (!server_active) + if (server_active) + return release_server (argc, argv); #endif - error (1, 0, - "-q or -Q must be specified before \"%s\"", - command_name); + + /* Everything from here on is client or local. */ + if (argc == -1) + usage (release_usage); + optind = 1; + while ((c = getopt (argc, argv, "+Qdq")) != -1) + { + switch (c) + { + case 'Q': + case 'q': + error (1, 0, + "-q or -Q must be specified before \"%s\"", + command_name); break; - case 'd': + case 'd': delete_flag++; break; - case '?': - default: + case '?': + default: usage (release_usage); break; - } - } - argc -= optind; - argv += optind; -#ifdef SERVER_SUPPORT - } -#endif /* SERVER_SUPPORT */ + } + } + argc -= optind; + argv += optind; /* We're going to run "cvs -n -q update" and check its output; if * the output is sufficiently unalarming, then we release with no * questions asked. Else we prompt, then maybe release. */ /* Construct the update command. */ + update_cmd = xmalloc (strlen (program_path) + + strlen (CVSroot_original) + + 20); sprintf (update_cmd, "%s -n -q -d %s update", - program_path, CVSroot); + program_path, CVSroot_original); #ifdef CLIENT_SUPPORT /* Start the server; we'll close it after looping. */ if (client_active) - { + { start_server (); ign_setup (); - } + } #endif /* CLIENT_SUPPORT */ - /* If !server_active, we already skipped over argv[0] in the "argc - -= optind;" statement above. But if server_active, we need to - skip it now. */ -#ifdef SERVER_SUPPORT - if (server_active) - arg_start_idx = 1; - else -#endif /* SERVER_SUPPORT */ - arg_start_idx = 0; + arg_start_idx = 0; for (i = arg_start_idx; i < argc; i++) { - thisarg = argv[i]; - -#ifdef SERVER_SUPPORT - if (server_active) - { - /* Just log the release -- all the interesting stuff happened - * on the client. - */ - history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ - } - else - { -#endif /* SERVER_SUPPORT */ - - /* - * If we are in a repository, do it. Else if we are in the parent of - * a directory with the same name as the module, "cd" into it and - * look for a repository there. - */ + thisarg = argv[i]; + if (isdir (thisarg)) { - if (chdir (thisarg) < 0) - { - if (!really_quiet) - error (0, 0, "can't chdir to: %s", thisarg); - continue; - } - if (!isdir (CVSADM)) - { - if (!really_quiet) - error (0, 0, "no repository module: %s", thisarg); - continue; - } + if (CVS_CHDIR (thisarg) < 0) + { + if (!really_quiet) + error (0, errno, "can't chdir to: %s", thisarg); + continue; + } + if (!isdir (CVSADM)) + { + if (!really_quiet) + error (0, 0, "no repository directory: %s", thisarg); + continue; + } } else { - if (!really_quiet) - error (0, 0, "no such directory: %s", thisarg); - continue; + if (!really_quiet) + error (0, 0, "no such directory: %s", thisarg); + continue; } repository = Name_Repository ((char *) NULL, (char *) NULL); - srepos = Short_Repository (repository); - + if (!really_quiet) { - /* The "release" command piggybacks on "update", which - * does the real work of finding out if anything is not - * up-to-date with the repository. Then "release" prompts - * the user, telling her how many files have been - * modified, and asking if she still wants to do the - * release. - */ - fp = run_popen (update_cmd, "r"); - c = 0; - - while (fgets (line, sizeof (line), fp)) - { - if (strchr ("MARCZ", *line)) - c++; - (void) printf (line); - } - - /* If the update exited with an error, then we just want to - * complain and go on to the next arg. Especially, we do - * not want to delete the local copy, since it's obviously - * not what the user thinks it is. - */ - if ((pclose (fp)) != 0) - { - error (0, 0, "unable to release `%s'", thisarg); - continue; - } - - (void) printf ("You have [%d] altered files in this repository.\n", - c); - (void) printf ("Are you sure you want to release %smodule `%s': ", - delete_flag ? "(and delete) " : "", thisarg); - c = !yesno (); - if (c) /* "No" */ - { - (void) fprintf (stderr, "** `%s' aborted by user choice.\n", - command_name); - free (repository); - continue; - } + /* The "release" command piggybacks on "update", which + does the real work of finding out if anything is not + up-to-date with the repository. Then "release" prompts + the user, telling her how many files have been + modified, and asking if she still wants to do the + release. */ + fp = run_popen (update_cmd, "r"); + c = 0; + + while (getline (&line, &line_allocated, fp) >= 0) + { + if (strchr ("MARCZ", *line)) + c++; + (void) printf (line); + } + + /* If the update exited with an error, then we just want to + complain and go on to the next arg. Especially, we do + not want to delete the local copy, since it's obviously + not what the user thinks it is. */ + if ((pclose (fp)) != 0) + { + error (0, 0, "unable to release `%s'", thisarg); + continue; + } + + printf ("You have [%d] altered files in this repository.\n", + c); + printf ("Are you sure you want to release %sdirectory `%s': ", + delete_flag ? "(and delete) " : "", thisarg); + c = !yesno (); + if (c) /* "No" */ + { + (void) fprintf (stderr, "** `%s' aborted by user choice.\n", + command_name); + free (repository); + continue; + } } if (1 -#ifdef SERVER_SUPPORT - && !server_active -#endif #ifdef CLIENT_SUPPORT && !(client_active && (!supported_request ("noop") @@ -215,45 +204,53 @@ release (argc, argv) #endif ) { - /* We are chdir'ed into the directory in question. - So don't pass args to unedit. */ - int argc = 1; - char *argv[3]; - argv[0] = "dummy"; - argv[1] = NULL; - err += unedit (argc, argv); + /* We are chdir'ed into the directory in question. + So don't pass args to unedit. */ + int argc = 1; + char *argv[3]; + argv[0] = "dummy"; + argv[1] = NULL; + err += unedit (argc, argv); } #ifdef CLIENT_SUPPORT if (client_active) { - send_to_server ("Argument ", 0); - send_to_server (thisarg, 0); - send_to_server ("\012", 1); - send_to_server ("release\012", 0); - } + send_to_server ("Argument ", 0); + send_to_server (thisarg, 0); + send_to_server ("\012", 1); + send_to_server ("release\012", 0); + } else - { #endif /* CLIENT_SUPPORT */ - history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ -#ifdef CLIENT_SUPPORT - } /* else client not active */ -#endif /* CLIENT_SUPPORT */ - + { + history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ + } + free (repository); if (delete_flag) release_delete (thisarg); - + #ifdef CLIENT_SUPPORT if (client_active) - return get_responses_and_close (); - else + err += get_server_responses (); #endif /* CLIENT_SUPPORT */ - return (0); - -#ifdef SERVER_SUPPORT - } /* else server not active */ -#endif /* SERVER_SUPPORT */ - } /* `for' loop */ + } + +#ifdef CLIENT_SUPPORT + if (client_active) + { + /* Unfortunately, client.c doesn't offer a way to close + the connection without waiting for responses. The extra + network turnaround here is quite unnecessary other than + that.... */ + send_to_server ("noop\012", 0); + err += get_responses_and_close (); + } +#endif /* CLIENT_SUPPORT */ + + free (update_cmd); + if (line != NULL) + free (line); return err; } @@ -267,10 +264,10 @@ release_delete (dir) struct stat st; ino_t ino; - (void) stat (".", &st); + (void) CVS_STAT (".", &st); ino = st.st_ino; - (void) chdir (".."); - (void) stat (dir, &st); + (void) CVS_CHDIR (".."); + (void) CVS_STAT (dir, &st); if (ino != st.st_ino) { error (0, 0, diff --git a/contrib/cvs/src/remove.c b/contrib/cvs/src/remove.c index 2911bf4..1571250 100644 --- a/contrib/cvs/src/remove.c +++ b/contrib/cvs/src/remove.c @@ -17,8 +17,14 @@ #include "cvs.h" -static int remove_fileproc PROTO((struct file_info *finfo)); -static Dtype remove_dirproc PROTO((char *dir, char *repos, char *update_dir)); +#ifdef CLIENT_SUPPORT +static int remove_force_fileproc PROTO ((void *callerdat, + struct file_info *finfo)); +#endif +static int remove_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static Dtype remove_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); static int force; static int local; @@ -45,7 +51,7 @@ cvsremove (argc, argv) usage (remove_usage); optind = 1; - while ((c = getopt (argc, argv, "flR")) != -1) + while ((c = getopt (argc, argv, "+flR")) != -1) { switch (c) { @@ -71,12 +77,31 @@ cvsremove (argc, argv) #ifdef CLIENT_SUPPORT if (client_active) { + /* Call expand_wild so that the local removal of files will + work. It's ok to do it always because we have to send the + file names expanded anyway. */ + expand_wild (argc, argv, &argc, &argv); + + if (force) + { + if (!noexec) + { + start_recursion (remove_force_fileproc, (FILESDONEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (void *) NULL, argc, argv, local, W_LOCAL, + 0, 0, (char *) NULL, 0); + } + /* else FIXME should probably act as if the file doesn't exist + in doing the following checks. */ + } + start_server (); ign_setup (); if (local) send_arg("-l"); - send_file_names (argc, argv, SEND_EXPAND_WILD); - send_files (argc, argv, local, 0); + send_file_names (argc, argv, 0); + /* FIXME: Can't we set SEND_NO_CONTENTS here? Needs investigation. */ + send_files (argc, argv, local, 0, 0); send_to_server ("remove\012", 0); return get_responses_and_close (); } @@ -84,8 +109,9 @@ cvsremove (argc, argv) /* start the recursion processor */ err = start_recursion (remove_fileproc, (FILESDONEPROC) NULL, - remove_dirproc, (DIRLEAVEPROC) NULL, argc, argv, - local, W_LOCAL, 0, 1, (char *) NULL, 1, 0); + remove_dirproc, (DIRLEAVEPROC) NULL, NULL, + argc, argv, + local, W_LOCAL, 0, 1, (char *) NULL, 1); if (removed_files) error (0, 0, "use '%s commit' to remove %s permanently", program_name, @@ -101,22 +127,42 @@ cvsremove (argc, argv) return (err); } +#ifdef CLIENT_SUPPORT + +/* + * This is called via start_recursion if we are running as the client + * and the -f option was used. We just physically remove the file. + */ + +/*ARGSUSED*/ +static int +remove_force_fileproc (callerdat, finfo) + void *callerdat; + struct file_info *finfo; +{ + if (CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno)) + error (0, errno, "unable to remove %s", finfo->fullname); + return 0; +} + +#endif + /* * remove the file, only if it has already been physically removed */ /* ARGSUSED */ static int -remove_fileproc (finfo) +remove_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { - char fname[PATH_MAX]; Vers_TS *vers; if (force) { if (!noexec) { - if (unlink (finfo->file) < 0 && ! existence_error (errno)) + if ( CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno)) { error (0, errno, "unable to remove %s", finfo->fullname); } @@ -125,8 +171,7 @@ remove_fileproc (finfo) in doing the following checks. */ } - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL, - finfo->file, 0, 0, finfo->entries, finfo->rcs); + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); if (vers->ts_user != NULL) { @@ -142,11 +187,17 @@ remove_fileproc (finfo) } else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') { + char *fname; + /* * It's a file that has been added, but not commited yet. So, * remove the ,t file for it and scratch it from the * entries file. */ Scratch_Entry (finfo->entries, finfo->file); + fname = xmalloc (strlen (finfo->file) + + sizeof (CVSADM) + + sizeof (CVSEXT_LOG) + + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); (void) unlink_file (fname); if (!quiet) @@ -156,6 +207,7 @@ remove_fileproc (finfo) if (server_active) server_checked_in (finfo->file, finfo->update_dir, finfo->repository); #endif + free (fname); } else if (vers->vn_user[0] == '-') { @@ -165,7 +217,10 @@ remove_fileproc (finfo) } else { + char *fname; + /* Re-register it with a negative version number. */ + fname = xmalloc (strlen (vers->vn_user) + 5); (void) strcpy (fname, "-"); (void) strcat (fname, vers->vn_user); Register (finfo->entries, finfo->file, fname, vers->ts_rcs, vers->options, @@ -178,6 +233,7 @@ remove_fileproc (finfo) if (server_active) server_checked_in (finfo->file, finfo->update_dir, finfo->repository); #endif + free (fname); } freevers_ts (&vers); @@ -189,10 +245,12 @@ remove_fileproc (finfo) */ /* ARGSUSED */ static Dtype -remove_dirproc (dir, repos, update_dir) +remove_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "Removing %s", update_dir); diff --git a/contrib/cvs/src/repos.c b/contrib/cvs/src/repos.c index 7beaaba..2590df5 100644 --- a/contrib/cvs/src/repos.c +++ b/contrib/cvs/src/repos.c @@ -7,6 +7,7 @@ */ #include "cvs.h" +#include "getline.h" /* Determine the name of the RCS repository for directory DIR in the current working directory, or for the current working directory @@ -23,11 +24,10 @@ Name_Repository (dir, update_dir) char *update_dir; { FILE *fpin; - char *ret, *xupdate_dir; - char repos[PATH_MAX]; - char path[PATH_MAX]; - char tmp[PATH_MAX]; - char cvsadm[PATH_MAX]; + char *xupdate_dir; + char *repos = NULL; + size_t repos_allocated = 0; + char *tmp; char *cp; if (update_dir && *update_dir) @@ -36,48 +36,61 @@ Name_Repository (dir, update_dir) xupdate_dir = "."; if (dir != NULL) - (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); - else - (void) strcpy (cvsadm, CVSADM); - - /* sanity checks */ - if (!isdir (cvsadm)) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "there is no version here; do '%s checkout' first", - program_name); - } - - if (dir != NULL) - (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT); - else - (void) strcpy (tmp, CVSADM_ENT); - - if (!isreadable (tmp)) { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "*PANIC* administration files missing"); - } - - if (dir != NULL) + tmp = xmalloc (strlen (dir) + sizeof (CVSADM_REP) + 10); (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP); - else - (void) strcpy (tmp, CVSADM_REP); - - if (!isreadable (tmp)) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "*PANIC* administration files missing"); } + else + tmp = xstrdup (CVSADM_REP); /* * The assumption here is that the repository is always contained in the * first line of the "Repository" file. */ - fpin = open_file (tmp, "r"); + fpin = CVS_FOPEN (tmp, "r"); - if (fgets (repos, PATH_MAX, fpin) == NULL) + if (fpin == NULL) { + int save_errno = errno; + char *cvsadm; + + if (dir != NULL) + { + cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); + (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); + } + else + cvsadm = xstrdup (CVSADM); + + if (!isdir (cvsadm)) + { + error (0, 0, "in directory %s:", xupdate_dir); + error (1, 0, "there is no version here; do '%s checkout' first", + program_name); + } + free (cvsadm); + + if (existence_error (save_errno)) + { + /* FIXME: This is a very poorly worded error message. It + occurs at least in the case where the user manually + creates a directory named CVS, so the error message + should be more along the lines of "CVS directory found + without administrative files; use CVS to create the CVS + directory, or rename it to something else if the + intention is to store something besides CVS + administrative files". */ + error (0, 0, "in directory %s:", xupdate_dir); + error (1, 0, "*PANIC* administration files missing"); + } + + error (1, save_errno, "cannot open %s", tmp); + } + free (tmp); + + if (getline (&repos, &repos_allocated, fpin) < 0) + { + /* FIXME: should be checking for end of file separately. */ error (0, 0, "in directory %s:", xupdate_dir); error (1, errno, "cannot read %s", CVSADM_REP); } @@ -98,30 +111,23 @@ Name_Repository (dir, update_dir) } if (! isabsolute(repos)) { - if (CVSroot == NULL) + char *newrepos; + + if (CVSroot_original == NULL) { error (0, 0, "in directory %s:", xupdate_dir); error (0, 0, "must set the CVSROOT environment variable\n"); error (0, 0, "or specify the '-d' option to %s.", program_name); error (1, 0, "illegal repository setting"); } - (void) strcpy (path, repos); - (void) sprintf (repos, "%s/%s", CVSroot, path); - } -#ifdef CLIENT_SUPPORT - if (!client_active && !isdir (repos)) -#else - if (!isdir (repos)) -#endif - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "there is no repository %s", repos); + newrepos = xmalloc (strlen (CVSroot_directory) + strlen (repos) + 10); + (void) sprintf (newrepos, "%s/%s", CVSroot_directory, repos); + free (repos); + repos = newrepos; } - /* allocate space to return and fill it in */ - strip_path (repos); - ret = xstrdup (repos); - return (ret); + strip_trailing_slashes (repos); + return repos; } /* @@ -137,9 +143,10 @@ Short_Repository (repository) /* If repository matches CVSroot at the beginning, strip off CVSroot */ /* And skip leading '/' in rep, in case CVSroot ended with '/'. */ - if (strncmp (CVSroot, repository, strlen (CVSroot)) == 0) + if (strncmp (CVSroot_directory, repository, + strlen (CVSroot_directory)) == 0) { - char *rep = repository + strlen (CVSroot); + char *rep = repository + strlen (CVSroot_directory); return (*rep == '/') ? rep+1 : rep; } else diff --git a/contrib/cvs/src/root.c b/contrib/cvs/src/root.c index 0742cf0..2691032 100644 --- a/contrib/cvs/src/root.c +++ b/contrib/cvs/src/root.c @@ -12,6 +12,16 @@ */ #include "cvs.h" +#include "getline.h" + +/* Printable names for things in the CVSroot_method enum variable. + Watch out if the enum is changed in cvs.h! */ + +char *method_names[] = { + "local", "server (rsh)", "pserver", "kserver", "ext" +}; + +#ifndef DEBUG char * Name_Root(dir, update_dir) @@ -20,9 +30,10 @@ Name_Root(dir, update_dir) { FILE *fpin; char *ret, *xupdate_dir; - char root[PATH_MAX]; - char tmp[PATH_MAX]; - char cvsadm[PATH_MAX]; + char *root = NULL; + size_t root_allocated = 0; + char *tmp; + char *cvsadm; char *cp; if (update_dir && *update_dir) @@ -32,13 +43,15 @@ Name_Root(dir, update_dir) if (dir != NULL) { + cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); + tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); } else { - (void) strcpy (cvsadm, CVSADM); - (void) strcpy (tmp, CVSADM_ROOT); + cvsadm = xstrdup (CVSADM); + tmp = xstrdup (CVSADM_ROOT); } /* @@ -48,17 +61,11 @@ Name_Root(dir, update_dir) * It is possible that not all repositories will have a CVS/Root * file. This is ok, but the user will need to specify -d * /path/name or have the environment variable CVSROOT set in - * order to continue. - */ + * order to continue. */ if ((!isdir (cvsadm)) || (!isreadable (tmp))) { - if (CVSroot == NULL) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (0, 0, "must set the CVSROOT environment variable"); - error (0, 0, "or specify the '-d' option to %s.", program_name); - } - return (NULL); + ret = NULL; + goto out; } /* @@ -67,12 +74,15 @@ Name_Root(dir, update_dir) */ fpin = open_file (tmp, "r"); - if (fgets (root, PATH_MAX, fpin) == NULL) + if (getline (&root, &root_allocated, fpin) < 0) { + /* FIXME: should be checking for end of file separately; errno + is not set in that case. */ error (0, 0, "in directory %s:", xupdate_dir); error (0, errno, "cannot read %s", CVSADM_ROOT); error (0, 0, "please correct this problem"); - return (NULL); + ret = NULL; + goto out; } (void) fclose (fpin); if ((cp = strrchr (root, '\n')) != NULL) @@ -80,22 +90,21 @@ Name_Root(dir, update_dir) /* * root now contains a candidate for CVSroot. It must be an - * absolute pathname + * absolute pathname or specify a remote server. */ + if ( #ifdef CLIENT_SUPPORT - /* It must specify a server via remote CVS or be an absolute pathname. */ - if ((strchr (root, ':') == NULL) - && ! isabsolute (root)) -#else /* ! CLIENT_SUPPORT */ - if (root[0] != '/') -#endif /* CLIENT_SUPPORT */ + (strchr (root, ':') == NULL) && +#endif + ! isabsolute (root)) { error (0, 0, "in directory %s:", xupdate_dir); error (0, 0, "ignoring %s because it does not contain an absolute pathname.", CVSADM_ROOT); - return (NULL); + ret = NULL; + goto out; } #ifdef CLIENT_SUPPORT @@ -108,12 +117,18 @@ Name_Root(dir, update_dir) error (0, 0, "ignoring %s because it specifies a non-existent repository %s", CVSADM_ROOT, root); - return (NULL); + ret = NULL; + goto out; } /* allocate space to return and fill it in */ - strip_path (root); + strip_trailing_slashes (root); ret = xstrdup (root); + out: + free (cvsadm); + free (tmp); + if (root != NULL) + free (root); return (ret); } @@ -130,9 +145,9 @@ same_directories (dir1, dir2) struct stat sb2; int ret; - if (stat (dir1, &sb1) < 0) + if ( CVS_STAT (dir1, &sb1) < 0) return (0); - if (stat (dir2, &sb2) < 0) + if ( CVS_STAT (dir2, &sb2) < 0) return (0); ret = 0; @@ -155,7 +170,7 @@ Create_Root (dir, rootdir) char *rootdir; { FILE *fout; - char tmp[PATH_MAX]; + char *tmp; if (noexec) return; @@ -165,13 +180,294 @@ Create_Root (dir, rootdir) if (rootdir != NULL) { if (dir != NULL) + { + tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); + } else - (void) strcpy (tmp, CVSADM_ROOT); + tmp = xstrdup (CVSADM_ROOT); + fout = open_file (tmp, "w+"); if (fprintf (fout, "%s\n", rootdir) < 0) error (1, errno, "write to %s failed", tmp); if (fclose (fout) == EOF) error (1, errno, "cannot close %s", tmp); + free (tmp); + } +} + +#endif /* ! DEBUG */ + + +/* Parse a CVSROOT variable into its constituent parts -- method, + * username, hostname, directory. The prototypical CVSROOT variable + * looks like: + * + * :method:user@host:path + * + * Some methods may omit fields; local, for example, doesn't need user + * and host. + * + * Returns zero on success, non-zero on failure. */ + +char *CVSroot_original = NULL; /* the CVSroot that was passed in */ +int client_active; /* nonzero if we are doing remote access */ +CVSmethod CVSroot_method; /* one of the enum values defined in cvs.h */ +char *CVSroot_username; /* the username or NULL if method == local */ +char *CVSroot_hostname; /* the hostname or NULL if method == local */ +char *CVSroot_directory; /* the directory name */ + +#ifdef AUTH_SERVER_SUPPORT +/* Die if CVSroot_directory and Pserver_Repos don't match. */ +static void +check_root_consistent () +{ + /* FIXME: Should be using a deferred error, as the rest of + serve_root does. As it is now the call to error could conceivably + cause deadlock, as noted in server_cleanup. Best solution would + presumably be to write some code so that error() automatically + defers the error in those cases where that is needed. */ + /* FIXME? Possible that the wording should be more clear (e.g. + Root says "%s" but pserver protocol says "%s" + or something which would aid people who are writing implementations + of the client side of the CVS protocol. I don't see any security + problem with revealing that information. */ + if ((Pserver_Repos != NULL) && (CVSroot_directory != NULL)) + if (strcmp (Pserver_Repos, CVSroot_directory) != 0) + error (1, 0, "repository mismatch: \"%s\" vs \"%s\"", + Pserver_Repos, CVSroot_directory); +} + +#endif /* AUTH_SERVER_SUPPORT */ + + +int +parse_cvsroot (CVSroot) + char *CVSroot; +{ + static int cvsroot_parsed = 0; + char *cvsroot_copy, *p; + + /* Don't go through the trouble twice. */ + if (cvsroot_parsed) + { + error (0, 0, "WARNING (parse_cvsroot): someone called me twice!\n"); + return 0; + } + + CVSroot_original = xstrdup (CVSroot); + cvsroot_copy = xstrdup (CVSroot); + + if ((*cvsroot_copy == ':')) + { + char *method = ++cvsroot_copy; + + /* Access method specified, as in + * "cvs -d :pserver:user@host:/path", + * "cvs -d :local:e:\path", or + * "cvs -d :kserver:user@host:/path". + * We need to get past that part of CVSroot before parsing the + * rest of it. + */ + + if (! (p = strchr (method, ':'))) + { + error (0, 0, "bad CVSroot: %s", CVSroot); + return 1; + } + *p = '\0'; + cvsroot_copy = ++p; + + /* Now we have an access method -- see if it's valid. */ + + if (strcmp (method, "local") == 0) + CVSroot_method = local_method; + else if (strcmp (method, "pserver") == 0) + CVSroot_method = pserver_method; + else if (strcmp (method, "kserver") == 0) + CVSroot_method = kserver_method; + else if (strcmp (method, "server") == 0) + CVSroot_method = server_method; + else if (strcmp (method, "ext") == 0) + CVSroot_method = ext_method; + else + { + error (0, 0, "unknown method in CVSroot: %s", CVSroot); + return 1; + } + } + else + { + /* If the method isn't specified, assume + SERVER_METHOD/EXT_METHOD if the string contains a colon or + LOCAL_METHOD otherwise. */ + + CVSroot_method = ((strchr (cvsroot_copy, ':')) +#ifdef RSH_NOT_TRANSPARENT + ? server_method +#else + ? ext_method +#endif + : local_method); + } + + client_active = (CVSroot_method != local_method); + + /* Check for username/hostname if we're not LOCAL_METHOD. */ + + CVSroot_username = NULL; + CVSroot_hostname = NULL; + + if (CVSroot_method != local_method) + { + /* Check to see if there is a username in the string. */ + + if ((p = strchr (cvsroot_copy, '@'))) + { + CVSroot_username = cvsroot_copy; + *p = '\0'; + cvsroot_copy = ++p; + if (*CVSroot_username == '\0') + CVSroot_username = NULL; + } + + if ((p = strchr (cvsroot_copy, ':'))) + { + CVSroot_hostname = cvsroot_copy; + *p = '\0'; + cvsroot_copy = ++p; + + if (*CVSroot_hostname == '\0') + CVSroot_hostname = NULL; + } + } + + CVSroot_directory = cvsroot_copy; +#ifdef AUTH_SERVER_SUPPORT + check_root_consistent (); +#endif /* AUTH_SERVER_SUPPORT */ + +#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) + if (CVSroot_method != local_method) + { + error (0, 0, "Your CVSROOT is set for a remote access method"); + error (0, 0, "but your CVS executable doesn't support it"); + error (0, 0, "(%s)", CVSroot); + return 1; + } +#endif + + /* Do various sanity checks. */ + + if (CVSroot_username && ! CVSroot_hostname) + { + error (0, 0, "missing hostname in CVSROOT: %s", CVSroot); + return 1; + } + + switch (CVSroot_method) + { + case local_method: + if (CVSroot_username || CVSroot_hostname) + { + error (0, 0, "can't specify hostname and username in CVSROOT"); + error (0, 0, "when using local access method"); + error (0, 0, "(%s)", CVSroot); + return 1; + } + /* cvs.texinfo has always told people that CVSROOT must be an + absolute pathname. Furthermore, attempts to use a relative + pathname produced various errors (I couldn't get it to work), + so there would seem to be little risk in making this a fatal + error. */ + if (!isabsolute (CVSroot_directory)) + error (1, 0, "CVSROOT %s must be an absolute pathname", + CVSroot_directory); + break; + case kserver_method: +#ifndef HAVE_KERBEROS + error (0, 0, "Your CVSROOT is set for a kerberos access method"); + error (0, 0, "but your CVS executable doesn't support it"); + error (0, 0, "(%s)", CVSroot); + return 1; +#endif + case server_method: + case ext_method: + case pserver_method: + if (! CVSroot_hostname) + { + error (0, 0, "didn't specify hostname in CVSROOT: %s", CVSroot); + return 1; + } + break; } + + if (*CVSroot_directory == '\0') + { + error (0, 0, "missing directory in CVSROOT: %s", CVSroot); + return 1; + } + + /* Hooray! We finally parsed it! */ + return 0; +} + + +/* Set up the global CVSroot* variables as if we're using the local + repository DIR. */ + +void +set_local_cvsroot (dir) + char *dir; +{ + CVSroot_original = xstrdup (dir); + CVSroot_method = local_method; + CVSroot_directory = CVSroot_original; +#ifdef AUTH_SERVER_SUPPORT + check_root_consistent (); +#endif /* AUTH_SERVER_SUPPORT */ + CVSroot_username = NULL; + CVSroot_hostname = NULL; + client_active = 0; +} + + +#ifdef DEBUG +/* This is for testing the parsing function. */ + +#include <stdio.h> + +char *CVSroot; +char *program_name = "testing"; +char *command_name = "parse_cvsroot"; /* XXX is this used??? */ + +void +main (argc, argv) + int argc; + char *argv[]; +{ + program_name = argv[0]; + + if (argc != 2) + { + fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); + exit (2); + } + + if (parse_cvsroot (argv[1])) + { + fprintf (stderr, "%s: Parsing failed.", program_name); + exit (1); + } + printf ("CVSroot: %s\n", argv[1]); + printf ("CVSroot_method: %s\n", method_names[CVSroot_method]); + printf ("CVSroot_username: %s\n", + CVSroot_username ? CVSroot_username : "NULL"); + printf ("CVSroot_hostname: %s\n", + CVSroot_hostname ? CVSroot_hostname : "NULL"); + printf ("CVSroot_directory: %s\n", CVSroot_directory); + + exit (0); + /* NOTREACHED */ } +#endif diff --git a/contrib/cvs/src/rtag.c b/contrib/cvs/src/rtag.c index 8609647..1e2017b 100644 --- a/contrib/cvs/src/rtag.c +++ b/contrib/cvs/src/rtag.c @@ -13,15 +13,22 @@ #include "cvs.h" -static int check_fileproc PROTO((struct file_info *finfo)); -static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); +static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int check_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static int pretag_proc PROTO((char *repository, char *filter)); static void masterlist_delproc PROTO((Node *p)); static void tag_delproc PROTO((Node *p)); static int pretag_list_proc PROTO((Node *p, void *closure)); -static Dtype rtag_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int rtag_fileproc PROTO((struct file_info *finfo)); +static Dtype rtag_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int rtag_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static int rtag_proc PROTO((int *pargc, char **argv, char *xwhere, char *mwhere, char *mfile, int shorten, int local_specified, char *mname, char *msg)); @@ -85,7 +92,7 @@ rtag (argc, argv) usage (rtag_usage); optind = 1; - while ((c = getopt (argc, argv, "FanfQqlRdbr:D:")) != -1) + while ((c = getopt (argc, argv, "+FanfQqlRdbr:D:")) != -1) { switch (c) { @@ -157,9 +164,11 @@ rtag (argc, argv) { /* We're the client side. Fire up the remote server. */ start_server (); - + ign_setup (); + if (!force_tag_match) + send_arg ("-f"); if (local) send_arg("-l"); if (delete_flag) @@ -168,7 +177,7 @@ rtag (argc, argv) send_arg("-b"); if (force_tag_move) send_arg("-F"); - if (run_module_prog) + if (!run_module_prog) send_arg("-n"); if (attic_too) send_arg("-a"); @@ -222,19 +231,25 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, char *mname; char *msg; { + /* Begin section which is identical to patch_proc--should this + be abstracted out somehow? */ int err = 0; int which; - char repository[PATH_MAX]; - char where[PATH_MAX]; + char *repository; + char *where; - (void) sprintf (repository, "%s/%s", CVSroot, argv[0]); + repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile)) + 30); + (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); + where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile)) + + 10); (void) strcpy (where, argv[0]); /* if mfile isn't null, we need to set up to do only part of the module */ if (mfile != NULL) { char *cp; - char path[PATH_MAX]; + char *path; /* if the portion of the module is a path, put the dir part on repos */ if ((cp = strrchr (mfile, '/')) != NULL) @@ -248,6 +263,7 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, } /* take care of the rest */ + path = xmalloc (strlen (repository) + strlen (mfile) + 5); (void) sprintf (path, "%s/%s", repository, mfile); if (isdir (path)) { @@ -266,14 +282,18 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, argv[1] = xstrdup (mfile); (*pargc) = 2; } + free (path); } - /* chdir to the starting directory */ - if (chdir (repository) < 0) + /* cd to the starting repository */ + if ( CVS_CHDIR (repository) < 0) { error (0, errno, "cannot chdir to %s", repository); + free (repository); return (1); } + free (repository); + /* End section which is identical to patch_proc. */ if (delete_flag || attic_too || (force_tag_match && numtag)) which = W_REPOS | W_ATTIC; @@ -291,9 +311,9 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, mtlist = getlist(); err = start_recursion (check_fileproc, check_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, *pargc - 1, argv + 1, local, which, 0, 1, - where, 1, 1); + where, 1); if (err) { @@ -301,10 +321,11 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, } /* start the recursion processor */ - err = start_recursion (rtag_fileproc, (FILESDONEPROC) NULL, rtag_dirproc, - (DIRLEAVEPROC) NULL, *pargc - 1, argv + 1, local, - which, 0, 1, where, 1, 1); - + err = start_recursion (rtag_fileproc, rtag_filesdoneproc, rtag_dirproc, + (DIRLEAVEPROC) NULL, NULL, + *pargc - 1, argv + 1, local, + which, 0, 0, where, 1); + free (where); dellist(&mtlist); return (err); @@ -314,7 +335,8 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, /* All we do here is add it to our list */ static int -check_fileproc (finfo) +check_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *xdir; @@ -349,15 +371,16 @@ check_fileproc (finfo) p->key = xstrdup (finfo->file); p->type = UPDATE; p->delproc = tag_delproc; - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, - (char *) NULL, finfo->file, 0, 0, finfo->entries, finfo->rcs); - p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0); + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); + p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, + (int *) NULL); if (p->data != NULL) { int addit = 1; char *oversion; - oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); if (oversion == NULL) { if (delete_flag) @@ -389,10 +412,12 @@ check_fileproc (finfo) } static int -check_filesdoneproc(err, repos, update_dir) +check_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; int err; char *repos; char *update_dir; + List *entries; { int n; Node *p; @@ -496,13 +521,26 @@ pretag_list_proc(p, closure) */ /* ARGSUSED */ static int -rtag_fileproc (finfo) +rtag_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { RCSNode *rcsfile; char *version, *rev; int retcode = 0; + /* Lock the directory if it is not already locked. We might be + able to rely on rtag_dirproc for this. */ + + /* It would be nice to provide consistency with respect to + commits; however CVS lacks the infrastructure to do that (see + Concurrency in cvs.texinfo and comment in do_recursion). We + can and will prevent simultaneous tag operations from + interfering with each other, by write locking each directory as + we enter it, and unlocking it as we leave it. */ + + lock_dir_for_write (finfo->repository); + /* find the parsed RCS data */ if ((rcsfile = finfo->rcs) == NULL) return (1); @@ -527,7 +565,8 @@ rtag_fileproc (finfo) return (rtag_delete (rcsfile)); } - version = RCS_getversion (rcsfile, numtag, date, force_tag_match, 0); + version = RCS_getversion (rcsfile, numtag, date, force_tag_match, + (int *) NULL); if (version == NULL) { /* If -a specified, clean up any old tags */ @@ -554,7 +593,7 @@ rtag_fileproc (finfo) * the branch. Use a symbolic tag for that. */ rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag; - retcode = RCS_settag(rcsfile->path, symtag, numtag); + retcode = RCS_settag(rcsfile, symtag, numtag); } else { @@ -570,7 +609,8 @@ rtag_fileproc (finfo) * typical tagging operation. */ rev = branch_mode ? RCS_magicrev (rcsfile, version) : version; - oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0); + oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, + (int *) NULL); if (oversion != NULL) { int isbranch = RCS_isbranch (finfo->rcs, symtag); @@ -602,7 +642,7 @@ rtag_fileproc (finfo) } free (oversion); } - retcode = RCS_settag(rcsfile->path, symtag, rev); + retcode = RCS_settag(rcsfile, symtag, rev); } if (retcode != 0) @@ -641,18 +681,20 @@ rtag_delete (rcsfile) if (numtag) { - version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, 0); + version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, + (int *) NULL); if (version == NULL) return (0); free (version); } - version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0); + version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, + (int *) NULL); if (version == NULL) return (0); free (version); - if ((retcode = RCS_deltag(rcsfile->path, symtag, 1)) != 0) + if ((retcode = RCS_deltag(rcsfile, symtag, 1)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -663,15 +705,32 @@ rtag_delete (rcsfile) return (0); } +/* Clear any lock we may hold on the current directory. */ + +static int +rtag_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; + int err; + char *repos; + char *update_dir; + List *entries; +{ + Lock_Cleanup (); + + return (err); +} + /* * Print a warm fuzzy message */ /* ARGSUSED */ static Dtype -rtag_dirproc (dir, repos, update_dir) +rtag_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); diff --git a/contrib/cvs/src/run.c b/contrib/cvs/src/run.c index 036821e..74e418d 100644 --- a/contrib/cvs/src/run.c +++ b/contrib/cvs/src/run.c @@ -10,11 +10,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #include "cvs.h" @@ -32,10 +28,11 @@ #endif static void run_add_arg PROTO((const char *s)); -static void run_init_prog PROTO((void)); extern char *strtok (); +extern int vasprintf (); + /* * To exec a program under CVS, first call run_setup() to setup any initial * arguments. The options to run_setup are essentially like printf(). The @@ -49,7 +46,6 @@ extern char *strtok (); * The execvp() syscall will be used, so that the PATH is searched correctly. * File redirections can be performed in the call to run_exec(). */ -static char *run_prog; static char **run_argv; static int run_argc; static int run_argc_allocated; @@ -70,8 +66,7 @@ run_setup (fmt, va_alist) #endif char *cp; int i; - - run_init_prog (); + char *run_prog; /* clean out any malloc'ed values from run_argv */ for (i = 0; i < run_argc; i++) @@ -87,15 +82,18 @@ run_setup (fmt, va_alist) /* process the varargs into run_prog */ #ifdef HAVE_VPRINTF VA_START (args, fmt); - (void) vsprintf (run_prog, fmt, args); + (void) vasprintf (&run_prog, fmt, args); va_end (args); #else - (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8); + you lose #endif + if (run_prog == NULL) + error (1, 0, "out of memory"); /* put each word into run_argv, allocating it as we go */ for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t")) run_add_arg (cp); + free (run_prog); } void @@ -119,20 +117,22 @@ run_args (fmt, va_alist) #ifdef HAVE_VPRINTF va_list args; #endif - - run_init_prog (); + char *run_prog; /* process the varargs into run_prog */ #ifdef HAVE_VPRINTF VA_START (args, fmt); - (void) vsprintf (run_prog, fmt, args); + (void) vasprintf (&run_prog, fmt, args); va_end (args); #else - (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8); + you lose #endif + if (run_prog == NULL) + error (1, 0, "out of memory"); /* and add the (single) argument to the run_argv list */ run_add_arg (run_prog); + free (run_prog); } static void @@ -153,14 +153,6 @@ run_add_arg (s) run_argv[run_argc] = (char *) 0; /* not post-incremented on purpose! */ } -static void -run_init_prog () -{ - /* make sure that run_prog is allocated once */ - if (run_prog == (char *) 0) - run_prog = xmalloc (10 * 1024); /* 10K of args for _setup and _arg */ -} - int run_exec (stin, stout, sterr, flags) char *stin; @@ -192,12 +184,11 @@ run_exec (stin, stout, sterr, flags) if (trace) { #ifdef SERVER_SUPPORT - (void) fprintf (stderr, "%c-> system(", (server_active) ? 'S' : ' '); -#else - (void) fprintf (stderr, "-> system("); + cvs_outerr (server_active ? "S" : " ", 1); #endif + cvs_outerr ("-> system(", 0); run_print (stderr); - (void) fprintf (stderr, ")\n"); + cvs_outerr (")\n", 0); } if (noexec && (flags & RUN_REALLY) == 0) return (0); @@ -244,7 +235,14 @@ run_exec (stin, stout, sterr, flags) fflush (stdout); fflush (stderr); - /* The output files, if any, are now created. Do the fork and dups */ + /* The output files, if any, are now created. Do the fork and dups. + + We use vfork not so much for the sake of unices without + copy-on-write (such systems are rare these days), but for the + sake of systems without an MMU, which therefore can't do + copy-on-write (e.g. Amiga). The other solution is spawn (see + windows-NT/run.c). */ + #ifdef HAVE_VFORK pid = vfork (); #else @@ -393,12 +391,22 @@ run_print (fp) FILE *fp; { int i; + void (*outfn) PROTO ((const char *, size_t)); + + if (fp == stderr) + outfn = cvs_outerr; + else if (fp == stdout) + outfn = cvs_output; + else + error (1, 0, "internal error: bad argument to run_print"); for (i = 0; i < run_argc; i++) { - (void) fprintf (fp, "'%s'", run_argv[i]); + (*outfn) ("'", 1); + (*outfn) (run_argv[i], 0); + (*outfn) ("'", 1); if (i != run_argc - 1) - (void) fprintf (fp, " "); + (*outfn) (" ", 1); } } @@ -437,7 +445,11 @@ piped_child (command, tofdp, fromfdp) if (pipe (from_child_pipe) < 0) error (1, errno, "cannot create pipe"); +#ifdef HAVE_VFORK + pid = vfork (); +#else pid = fork (); +#endif if (pid < 0) error (1, errno, "cannot fork"); if (pid == 0) @@ -494,7 +506,11 @@ filter_stream_through_program (oldfd, dir, prog, pidp) if (pipe (p)) error (1, errno, "cannot create pipe"); +#ifdef HAVE_VFORK + newpid = vfork (); +#else newpid = fork (); +#endif if (pidp) *pidp = newpid; switch (newpid) diff --git a/contrib/cvs/src/sanity.sh b/contrib/cvs/src/sanity.sh index 4f80f90..38e32b4 100755 --- a/contrib/cvs/src/sanity.sh +++ b/contrib/cvs/src/sanity.sh @@ -13,10 +13,19 @@ # See TODO list at end of file. +# You can't run CVS as root; print a nice error message here instead +# of somewhere later, after making a mess. +case "`whoami`" in + "root" ) + echo "sanity.sh: test suite does not work correctly when run as root" >&2 + exit 1 + ;; +esac + # required to make this script work properly. unset CVSREAD -TESTDIR=/tmp/cvs-sanity +TESTDIR=${TESTDIR:-/tmp/cvs-sanity} # "debugger" #set -x @@ -26,6 +35,12 @@ echo 'This test should produce no other output than this line, and a final "OK". if test x"$1" = x"-r"; then shift remote=yes + # If we're going to do remote testing, make sure 'rsh' works first. + host="`hostname`" + if test "x`${CVS_RSH-rsh} $host -n 'echo hi'`" != "xhi"; then + echo "ERROR: cannot test remote CVS, because \`rsh $host' fails." >&2 + exit 1 + fi else remote=no fi @@ -61,6 +76,15 @@ shift # special characters we are probably in big trouble. PROG=`basename ${testcvs}` +# Regexp to match an author name. I'm not really sure what characters +# should be here. a-zA-Z obviously. People complained when 0-9 were +# not allowed in usernames. Other than that I'm not sure. +username="[a-zA-Z0-9][a-zA-Z0-9]*" + +# Regexp to match the name of a temporary file (from cvs_temp_name). +# This appears in certain diff output. +tempname="[-a-zA-Z0-9/.%_]*" + # FIXME: try things (what things? checkins?) without -m. # # Some of these tests are written to expect -Q. But testing with @@ -79,18 +103,79 @@ if test -f check.log; then mv check.log check.plog fi +GEXPRLOCS="`echo $PATH | sed 's/:/ /g'` /usr/local/bin /usr/contrib/bin /usr/gnu/bin /local/bin /local/gnu/bin /gun/bin" + +EXPR=expr + +# Cause NextStep 3.3 users to lose in a more graceful fashion. +if $EXPR 'abc +def' : 'abc +def' >/dev/null; then + : good, it works +else + for path in $GEXPRLOCS ; do + if test -x $path/gexpr ; then + if test "X`$path/gexpr --version`" != "X--version" ; then + EXPR=$path/gexpr + break + fi + fi + if test -x $path/expr ; then + if test "X`$path/expr --version`" != "X--version" ; then + EXPR=$path/expr + break + fi + fi + done + if test -z "$EXPR" ; then + echo 'Running these tests requires an "expr" program that can handle' + echo 'multi-line patterns. Make sure that such an expr (GNU, or many but' + echo 'not all vendor-supplied versions) is in your path.' + exit 1 + fi +fi + +# Warn SunOS, SysVr3.2, etc., users that they may be partially losing +# if we can't find a GNU expr to ease their troubles... +if $EXPR 'a +b' : 'a +c' >/dev/null; then + for path in $GEXPRLOCS ; do + if test -x $path/gexpr ; then + if test "X`$path/gexpr --version`" != "X--version" ; then + EXPR=$path/gexpr + break + fi + fi + if test -x $path/expr ; then + if test "X`$path/expr --version`" != "X--version" ; then + EXPR=$path/expr + break + fi + fi + done + if test -z "$EXPR" ; then + echo 'Warning: you are using a version of expr which does not correctly' + echo 'match multi-line patterns. Some tests may spuriously pass.' + echo 'You may wish to make sure GNU expr is in your path.' + EXPR=expr + fi +else + : good, it works +fi + # That we should have to do this is total bogosity, but GNU expr -# version 1.9.4 uses the emacs definition of "$" instead of the unix +# version 1.9.4-1.12 uses the emacs definition of "$" instead of the unix # (e.g. SunOS 4.1.3 expr) one. Rumor has it this will be fixed in the # next release of GNU expr after 1.12 (but we still have to cater to the old # ones for some time because they are in many linux distributions). ENDANCHOR="$" -if expr 'abc +if $EXPR 'abc def' : 'abc$' >/dev/null; then ENDANCHOR='\'\' fi -# Work around another GNU expr (version 1.10) bug/incompatibility. +# Work around another GNU expr (version 1.10-1.12) bug/incompatibility. # "." doesn't appear to match a newline (it does with SunOS 4.1.3 expr). # Note that the workaround is not a complete equivalent of .* because # the first parenthesized expression in the regexp must match something @@ -99,7 +184,7 @@ fi # next release of GNU expr after 1.12 (but we still have to cater to the old # ones for some time because they are in many linux distributions). DOTSTAR='.*' -if expr 'abc +if $EXPR 'abc def' : "a${DOTSTAR}f" >/dev/null; then : good, it works else @@ -115,7 +200,7 @@ fi # next release of GNU expr after 1.12 (but we still have to cater to the old # ones for some time because they are in many linux distributions). PLUS='+' -if expr 'a +b' : "a ${PLUS}b" >/dev/null; then +if $EXPR 'a +b' : "a ${PLUS}b" >/dev/null; then : good, it works else PLUS='\+' @@ -123,35 +208,12 @@ fi # Likewise, for ? QUESTION='?' -if expr 'a?b' : "a${QUESTION}b" >/dev/null; then +if $EXPR 'a?b' : "a${QUESTION}b" >/dev/null; then : good, it works else QUESTION='\?' fi -# Cause NextStep 3.3 users to lose in a more graceful fashion. -if expr 'abc -def' : 'abc -def' >/dev/null; then - : good, it works -else - echo 'Running these tests requires an "expr" program that can handle' - echo 'multi-line patterns. Make sure that such an expr (GNU, or many but' - echo 'not all vendor-supplied versions) is in your path.' - exit 1 -fi - -# Warn SunOS, SysVr3.2, etc., users that they may be partially losing -if expr 'a -b' : 'a -c' >/dev/null; then - echo 'Warning: you are using a version of expr which does not correctly' - echo 'match multi-line patterns. Some tests may spuriously pass.' - echo 'You may wish to make sure GNU expr is in your path.' -else - : good, it works -fi - pass () { echo "PASS: $1" >>${LOGFILE} @@ -174,25 +236,122 @@ dotest_internal () if test -s ${TESTDIR}/dotest.tmp; then echo "** expected: " >>${LOGFILE} echo "$3" >>${LOGFILE} + echo "$3" > ${TESTDIR}/dotest.exp + rm -f ${TESTDIR}/dotest.ex2 echo "** got: " >>${LOGFILE} cat ${TESTDIR}/dotest.tmp >>${LOGFILE} fail "$1" else - cat ${TESTDIR}/dotest.tmp >>${LOGFILE} pass "$1" fi else - if expr "`cat ${TESTDIR}/dotest.tmp`" : \ + if $EXPR "`cat ${TESTDIR}/dotest.tmp`" : \ "$3"${ENDANCHOR} >/dev/null; then + # See below about writing this to the logfile. cat ${TESTDIR}/dotest.tmp >>${LOGFILE} pass "$1" else if test x"$4" != x; then - if expr "`cat ${TESTDIR}/dotest.tmp`" : \ + if $EXPR "`cat ${TESTDIR}/dotest.tmp`" : \ "$4"${ENDANCHOR} >/dev/null; then + # Why, I hear you ask, do we write this to the logfile + # even when the test passes? The reason is that the test + # may give us the regexp which we were supposed to match, + # but sometimes it may be useful to look at the exact + # text which was output. For example, suppose one wants + # to grep for a particular warning, and make _sure_ that + # CVS never hits it (even in cases where the tests might + # match it with .*). Or suppose one wants to see the exact + # date format output in a certain case (where the test will + # surely use a somewhat non-specific pattern). + cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + pass "$1" + else + echo "** expected: " >>${LOGFILE} + echo "$3" >>${LOGFILE} + echo "$3" > ${TESTDIR}/dotest.ex1 + echo "** or: " >>${LOGFILE} + echo "$4" >>${LOGFILE} + echo "$4" > ${TESTDIR}/dotest.ex2 + echo "** got: " >>${LOGFILE} cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + fail "$1" + fi + else + echo "** expected: " >>${LOGFILE} + echo "$3" >>${LOGFILE} + echo "$3" > ${TESTDIR}/dotest.exp + echo "** got: " >>${LOGFILE} + cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + fail "$1" + fi + fi + fi +} + +dotest_all_in_one () +{ + if $EXPR "`cat ${TESTDIR}/dotest.tmp`" : \ + "`cat ${TESTDIR}/dotest.exp`" >/dev/null; then + return 0 + fi + return 1 +} + +# WARNING: this won't work with REs that match newlines.... +# +dotest_line_by_line () +{ + line=1 + while [ $line -le `wc -l ${TESTDIR}/dotest.tmp` ] ; do + echo "$line matched \c" >>$LOGFILE + if $EXPR "`sed -n ${line}p ${TESTDIR}/dotest.tmp`" : \ + "`sed -n ${line}p ${TESTDIR}/dotest.exp`" >/dev/null; then + : + else + echo "**** expected line: " >>${LOGFILE} + sed -n ${line}p ${TESTDIR}/dotest.exp >>${LOGFILE} + echo "**** got line: " >>${LOGFILE} + sed -n ${line}p ${TESTDIR}/dotest.tmp >>${LOGFILE} + unset line + return 1 + fi + line=`expr $line + 1` + done + unset line + return 0 +} + +# If you are having trouble telling which line of a multi-line +# expression is not being matched, replace calls to dotest_internal() +# with calls to this function: +# +dotest_internal_debug () +{ + if test -z "$3"; then + if test -s ${TESTDIR}/dotest.tmp; then + echo "** expected: " >>${LOGFILE} + echo "$3" >>${LOGFILE} + echo "$3" > ${TESTDIR}/dotest.exp + rm -f ${TESTDIR}/dotest.ex2 + echo "** got: " >>${LOGFILE} + cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + fail "$1" + else + pass "$1" + fi + else + echo "$3" > ${TESTDIR}/dotest.exp + if dotest_line_by_line "$1" "$2"; then + pass "$1" + else + if test x"$4" != x; then + mv ${TESTDIR}/dotest.exp ${TESTDIR}/dotest.ex1 + echo "$4" > ${TESTDIR}/dotest.exp + if dotest_line_by_line "$1" "$2"; then pass "$1" else + mv ${TESTDIR}/dotest.exp ${TESTDIR}/dotest.ex2 echo "** expected: " >>${LOGFILE} echo "$3" >>${LOGFILE} echo "** or: " >>${LOGFILE} @@ -215,7 +374,7 @@ dotest_internal () # Usage: # dotest TESTNAME COMMAND OUTPUT [OUTPUT2] # TESTNAME is the name used in the log to identify the test. -# COMMAND is the command to run; for the test to pass, it exits with +# COMMAND is the command to run; for the test to pass, it exits with # exitstatus zero. # OUTPUT is a regexp which is compared against the output (stdout and # stderr combined) from the test. It is anchored to the start and end @@ -227,6 +386,7 @@ dotest_internal () # lack \|). dotest () { + rm -f ${TESTDIR}/dotest.ex? 2>&1 if $2 >${TESTDIR}/dotest.tmp 2>&1; then : so far so good else @@ -238,9 +398,34 @@ dotest () dotest_internal "$@" } +# Like dotest except only 2 args and result must exactly match stdin +dotest_lit () +{ + rm -f ${TESTDIR}/dotest.ex? 2>&1 + if $2 >${TESTDIR}/dotest.tmp 2>&1; then + : so far so good + else + status=$? + cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + echo "exit status was $status" >>${LOGFILE} + fail "$1" + fi + cat >${TESTDIR}/dotest.exp + if cmp ${TESTDIR}/dotest.exp ${TESTDIR}/dotest.tmp >/dev/null 2>&1; then + pass "$1" + else + echo "** expected: " >>${LOGFILE} + cat ${TESTDIR}/dotest.exp >>${LOGFILE} + echo "** got: " >>${LOGFILE} + cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + fail "$1" + fi +} + # Like dotest except exitstatus should be nonzero. dotest_fail () { + rm -f ${TESTDIR}/dotest.ex? 2>&1 if $2 >${TESTDIR}/dotest.tmp 2>&1; then status=$? cat ${TESTDIR}/dotest.tmp >>${LOGFILE} @@ -271,11 +456,20 @@ dotest_status () rm -rf ${TESTDIR} mkdir ${TESTDIR} cd ${TESTDIR} +# This will show up in cvs history output where it prints the working +# directory. It should *not* appear in any cvs output referring to the +# repository; cvs should use the name of the repository as specified. +TMPPWD=`/bin/pwd` # Avoid picking up any stray .cvsrc, etc., from the user running the tests mkdir home HOME=${TESTDIR}/home; export HOME +# Make sure this variable is not defined to anything that would +# change the format of rcs dates. Otherwise people using e.g., +# RCSINIT=-zLT get lots of spurious failures. +RCSINIT=; export RCSINIT + # Remaining arguments are the names of tests to run. # # The testsuite is broken up into (hopefully manageably-sized) @@ -284,76 +478,20 @@ HOME=${TESTDIR}/home; export HOME # tests. if test x"$*" = x; then - tests="basica basic1 deep basic2 death branches import new conflicts modules mflag errmsg1 devcom ignore binfiles info" + tests="basica basicb basic1 deep basic2 rdiff death death2 branches" + tests="${tests} multibranch import join new newb conflicts conflicts2" + tests="${tests} modules modules2 modules3 mflag errmsg1 devcom devcom2" + tests="${tests} devcom3 ignore binfiles binfiles2 binwrap info" + tests="${tests} serverpatch log log2 crerepos rcs big modes" + tests="${tests} sticky keyword" else tests="$*" fi -# this should die -if ${CVS} -d `pwd`/cvsroot co cvs-sanity 2>> ${LOGFILE} ; then - echo "FAIL: test 1" | tee -a ${LOGFILE} - exit 1 -else - echo "PASS: test 1" >>${LOGFILE} -fi - -# this should still die -mkdir cvsroot -if ${CVS} -d `pwd`/cvsroot co cvs-sanity 2>> ${LOGFILE} ; then - echo "FAIL: test 2" | tee -a ${LOGFILE} - exit 1 -else - echo "PASS: test 2" >>${LOGFILE} -fi - -# this should still die -mkdir cvsroot/CVSROOT -if ${CVS} -d `pwd`/cvsroot co cvs-sanity 2>> ${LOGFILE} ; then - echo "FAIL: test 3" | tee -a ${LOGFILE} - exit 1 -else - echo "PASS: test 3" >>${LOGFILE} -fi - -# This one should work, although it should spit a warning. -mkdir tmp ; cd tmp -${CVS} -d `pwd`/../cvsroot co CVSROOT 2>> ${LOGFILE} -cd .. ; rm -rf tmp - -# set up a minimal modules file... -# (now that mkmodules is gone, this doesn't test -i the way it -# used to. In fact, it looks like a noop to me). -echo "CVSROOT CVSROOT" > cvsroot/CVSROOT/modules -# The following line stolen from cvsinit.sh. FIXME: create our -# repository via cvsinit.sh; that way we test it too. -(cd cvsroot/CVSROOT; ci -q -u -t/dev/null \ - -m'initial checkin of modules' modules) - -# This one should succeed. No warnings. -mkdir tmp ; cd tmp -if ${CVS} -d `pwd`/../cvsroot co CVSROOT ; then - echo "PASS: test 4" >>${LOGFILE} -else - echo "FAIL: test 4" | tee -a ${LOGFILE} - exit 1 -fi - -if echo "yes" | ${CVS} -d `pwd`/../cvsroot release -d CVSROOT ; then - echo "PASS: test 4.5" >>${LOGFILE} -else - echo "FAIL: test 4.5" | tee -a ${LOGFILE} - exit 1 -fi -# this had better be empty -cd ..; rmdir tmp -dotest_fail 4.75 "test -d tmp" '' - # a simple function to compare directory contents # -# BTW, I don't care any more -- if you don't have a /bin/sh that handles -# shell functions, well get one. -# -# Returns: ISDIFF := true|false +# Returns: {nothing} +# Side Effects: ISDIFF := true|false # directory_cmp () { @@ -380,33 +518,28 @@ directory_cmp () cd $OLDPWD while read a do - if [ -f $DIR_1/"$a" ] ; then + if test -f $DIR_1/"$a" ; then cmp -s $DIR_1/"$a" $DIR_2/"$a" - if [ $? -ne 0 ] ; then + if test $? -ne 0 ; then ISDIFF=true fi fi done < /tmp/dc$$d1 -### FIXME: -### rm -f /tmp/dc$$* + rm -f /tmp/dc$$* } -# so much for the setup. Let's try something harder. - -# Try setting CVSROOT so we don't have to worry about it anymore. (now that -# we've tested -d cvsroot.) +# Set up CVSROOT (the crerepos tests will test operating without CVSROOT set). CVSROOT_DIRNAME=${TESTDIR}/cvsroot CVSROOT=${CVSROOT_DIRNAME} ; export CVSROOT if test "x$remote" = xyes; then - CVSROOT=`hostname`:${CVSROOT_DIRNAME} ; export CVSROOT - # Use rsh so we can test it without having to muck with inetd or anything - # like that. Also needed to get CVS_SERVER to work. - CVS_CLIENT_PORT=-1; export CVS_CLIENT_PORT + # Use rsh so we can test it without having to muck with inetd + # or anything like that. Also needed to get CVS_SERVER to + # work. + CVSROOT=:ext:`hostname`:${CVSROOT_DIRNAME} ; export CVSROOT CVS_SERVER=${testcvs}; export CVS_SERVER fi -# start keeping history -touch ${CVSROOT_DIRNAME}/CVSROOT/history +dotest 1 "${testcvs} init" '' ### The big loop for what in $tests; do @@ -416,14 +549,19 @@ for what in $tests; do # tests, but hopefully a lot faster. Also tests operating on # files two directories down *without* operating on the parent dirs. - # Using mkdir in the repository is used throughout these - # tests to create a top-level directory. I think instead it - # should be: - # cvs co -l . - # mkdir first-dir - # cvs add first-dir - # but currently that works only for local CVS, not remote. - mkdir ${CVSROOT_DIRNAME}/first-dir + # Tests basica-0a and basica-0b provide the equivalent of the: + # mkdir ${CVSROOT_DIRNAME}/first-dir + # used by many of the tests. It is "more official" in the sense + # that is does everything through CVS; the reason most of the + # tests don't use it is mostly historical. + mkdir 1; cd 1 + dotest basica-0a "${testcvs} -q co -l ." '' + mkdir first-dir + dotest basica-0b "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd .. + rm -rf 1 + dotest basica-1 "${testcvs} -q co first-dir" '' cd first-dir @@ -434,12 +572,20 @@ for what in $tests; do dotest basica-1a2 "${testcvs} -q status" '' mkdir sdir + # Remote CVS gives the "cannot open CVS/Entries" error, which is + # clearly a bug, but not a simple one to fix. + dotest basica-1a10 "${testcvs} -n add sdir" \ +"Directory ${TESTDIR}/cvsroot/first-dir/sdir added to the repository" \ +"${PROG} add: cannot open CVS/Entries for reading: No such file or directory +Directory ${TESTDIR}/cvsroot/first-dir/sdir added to the repository" + dotest_fail basica-1a11 \ + "test -d ${CVSROOT_DIRNAME}/first-dir/sdir" '' dotest basica-2 "${testcvs} add sdir" \ -'Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir added to the repository' +"Directory ${TESTDIR}/cvsroot/first-dir/sdir added to the repository" cd sdir mkdir ssdir dotest basica-3 "${testcvs} add ssdir" \ -'Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir added to the repository' +"Directory ${TESTDIR}/cvsroot/first-dir/sdir/ssdir added to the repository" cd ssdir echo ssfile >ssfile @@ -461,25 +607,38 @@ for what in $tests; do ${PROG} "'\[[a-z]* aborted\]: correct the above errors first!' cd ../.. dotest basica-5 "${testcvs} -q ci -m add-it" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v +"RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v done Checking in sdir/ssdir/ssfile; -/tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile -initial revision: 1.1 -done' +${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile +initial revision: 1\.1 +done" dotest_fail basica-5a \ "${testcvs} -q tag BASE sdir/ssdir/ssfile" \ "${PROG} [a-z]*: Attempt to add reserved tag name BASE -${PROG} \[[a-z]* aborted\]: failed to set tag BASE to revision 1.1 in /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v" +${PROG} \[[a-z]* aborted\]: failed to set tag BASE to revision 1\.1 in ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v" dotest basica-5b "${testcvs} -q tag NOT_RESERVED" \ 'T sdir/ssdir/ssfile' dotest basica-6 "${testcvs} -q update" '' echo "ssfile line 2" >>sdir/ssdir/ssfile dotest_status basica-6.2 1 "${testcvs} -q diff -c" \ -'Index: sdir/ssdir/ssfile +"Index: sdir/ssdir/ssfile +=================================================================== +RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v +retrieving revision 1\.1 +diff -c -r1\.1 ssfile +\*\*\* ssfile [0-9/]* [0-9:]* 1\.1 +--- ssfile [0-9/]* [0-9:]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +--- 1,2 ---- + ssfile +${PLUS} ssfile line 2" + dotest_status basica-6.3 1 "${testcvs} -q diff -c -rBASE" \ +"Index: sdir/ssdir/ssfile =================================================================== -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v +RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v retrieving revision 1\.1 diff -c -r1\.1 ssfile \*\*\* ssfile [0-9/]* [0-9:]* 1\.1 @@ -488,140 +647,654 @@ diff -c -r1\.1 ssfile \*\*\* 1 \*\*\*\* --- 1,2 ---- ssfile -'"${PLUS} ssfile line 2" +${PLUS} ssfile line 2" dotest basica-7 "${testcvs} -q ci -m modify-it" \ -'Checking in sdir/ssdir/ssfile; -/tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile -new revision: 1.2; previous revision: 1.1 -done' +"Checking in sdir/ssdir/ssfile; +${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile +new revision: 1\.2; previous revision: 1\.1 +done" dotest_fail basica-nonexist "${testcvs} -q ci nonexist" \ "${PROG}"' [a-z]*: nothing known about `nonexist'\'' '"${PROG}"' \[[a-z]* aborted\]: correct above errors first!' dotest basica-8 "${testcvs} -q update" '' + + # Test the -f option to ci + cd sdir/ssdir + dotest basica-8a0 "${testcvs} -q ci -m not-modified ssfile" '' + dotest basica-8a "${testcvs} -q ci -f -m force-it" \ +"Checking in ssfile; +${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile +new revision: 1\.3; previous revision: 1\.2 +done" + dotest basica-8a1 "${testcvs} -q ci -m bump-it -r 2.0" \ +"Checking in ssfile; +${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile +new revision: 2\.0; previous revision: 1\.3 +done" + # -f should not be necessary, but it should be harmless. + # Also test the "-r 3" (rather than "-r 3.0") usage. + dotest basica-8a2 "${testcvs} -q ci -m bump-it -f -r 3" \ +"Checking in ssfile; +${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile +new revision: 3\.1; previous revision: 2\.0 +done" + cd ../.. + dotest basica-8b "${testcvs} -q diff -r1.2 -r1.3" \ +"Index: sdir/ssdir/ssfile +=================================================================== +RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v +retrieving revision 1\.2 +retrieving revision 1\.3 +diff -r1\.2 -r1\.3" + + # The .* here will normally be "No such file or directory", + # but if memory serves some systems (AIX?) have a different message. +: dotest_fail basica-9 \ + "${testcvs} -q -d ${TESTDIR}/nonexist update" \ +"${PROG}: cannot access cvs root ${TESTDIR}/nonexist: .*" dotest_fail basica-9 \ - "${testcvs} -q -d /tmp/cvs-sanity/nonexist update" \ -"${PROG}: .*/tmp/cvs-sanity/cvsroot value for CVS Root found in CVS/Root -${PROG}"': does not match command line -d /tmp/cvs-sanity/nonexist setting -'"${PROG}"': you may wish to try the cvs command again without the -d option ' + "${testcvs} -q -d ${TESTDIR}/nonexist update" \ +"${PROG} \[[a-z]* aborted\]: ${TESTDIR}/nonexist/CVSROOT: .*" dotest basica-10 "${testcvs} annotate" \ 'Annotations for sdir/ssdir/ssfile \*\*\*\*\*\*\*\*\*\*\*\*\*\*\* -1.1 .[a-z@][a-z@ ]* [0-9a-zA-Z-]*.: ssfile -1.2 .[a-z@][a-z@ ]* [0-9a-zA-Z-]*.: ssfile line 2' +1\.1 .'"${username}"' *[0-9a-zA-Z-]*.: ssfile +1\.2 .'"${username}"' *[0-9a-zA-Z-]*.: ssfile line 2' cd .. rm -rf ${CVSROOT_DIRNAME}/first-dir rm -r first-dir ;; - basic1) # first dive - add a files, first singly, then in a group. - mkdir ${CVSROOT_DIRNAME}/first-dir - # check out an empty directory - if ${CVS} co first-dir ; then - echo "PASS: test 13a" >>${LOGFILE} - else - echo "FAIL: test 13a" | tee -a ${LOGFILE}; exit 1 - fi + basicb) + # More basic tests, including non-branch tags and co -d. + mkdir 1; cd 1 + dotest basicb-0a "${testcvs} -q co -l ." '' + touch topfile + dotest basicb-0b "${testcvs} add topfile" \ +"${PROG} [a-z]*: scheduling file .topfile. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest basicb-0c "${testcvs} -q ci -m add-it topfile" \ +"RCS file: ${TESTDIR}/cvsroot/./topfile,v +done +Checking in topfile; +${TESTDIR}/cvsroot/./topfile,v <-- topfile +initial revision: 1\.1 +done" + cd .. + rm -rf 1 + mkdir 2; cd 2 + dotest basicb-0d "${testcvs} -q co -l ." "U topfile" + mkdir first-dir + dotest basicb-0e "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd .. + rm -rf 2 - cd first-dir - files=first-file - for i in a b ; do - for j in ${files} ; do - echo $j > $j - done +: mkdir ${CVSROOT_DIRNAME}/first-dir + dotest basicb-1 "${testcvs} -q co first-dir" '' + dotest basicb-1a "test -d CVS" '' + # See comment at modules3-7f for more on this behavior. + dotest basicb-1b "cat CVS/Repository" \ +"${TESTDIR}/cvsroot/first-dir" "${TESTDIR}/cvsroot/\." + dotest basicb-1c "cat first-dir/CVS/Repository" \ +"${TESTDIR}/cvsroot/first-dir" - for do in add rm ; do - for j in ${do} "commit -m test" ; do - # ${do} - if ${CVS} $j ${files} >> ${LOGFILE} 2>&1; then - echo "PASS: test 14-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 14-${do}-$j" | tee -a ${LOGFILE}; exit 1 - fi - - # update it. - if [ "${do}" = "rm" -a "$j" != "commit -m test" ] || ${CVS} update ${files} ; then - echo "PASS: test 15-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 15-${do}-$j" | tee -a ${LOGFILE}; exit 1 - fi - - # update all. - if ${CVS} update ; then - echo "PASS: test 16-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 16-${do}-$j" | tee -a ${LOGFILE}; exit 1 - fi - - # status all. - if ${CVS} status >> ${LOGFILE}; then - echo "PASS: test 17-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 17-${do}-$j" | tee -a ${LOGFILE}; exit 1 - fi - - # FIXME: this one doesn't work yet for added files. - # log all. - if ${CVS} log >> ${LOGFILE}; then - echo "PASS: test 18-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 18-${do}-$j" | tee -a ${LOGFILE} - fi - - cd .. - # update all. - if ${CVS} update ; then - echo "PASS: test 21-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 21-${do}-$j" | tee -a ${LOGFILE}; exit 1 - fi - - # log all. - # FIXME: doesn't work right for added files. - if ${CVS} log first-dir >> ${LOGFILE}; then - echo "PASS: test 22-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 22-${do}-$j" | tee -a ${LOGFILE} - fi - - # status all. - if ${CVS} status first-dir >> ${LOGFILE}; then - echo "PASS: test 23-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 23-${do}-$j" | tee -a ${LOGFILE}; exit 1 - fi - - # update all. - if ${CVS} update first-dir ; then - echo "PASS: test 24-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 24-${do}-$j" | tee -a ${LOGFILE} ; exit 1 - fi - - # update all. - if ${CVS} co first-dir ; then - echo "PASS: test 27-${do}-$j" >>${LOGFILE} - else - echo "FAIL: test 27-${do}-$j" | tee -a ${LOGFILE} ; exit 1 - fi - - cd first-dir - done # j - rm -f ${files} - done # do - - files="file2 file3 file4 file5" - done - if ${CVS} tag first-dive ; then - echo "PASS: test 28" >>${LOGFILE} - else - echo "FAIL: test 28" | tee -a ${LOGFILE} ; exit 1 - fi - cd .. - rm -rf ${CVSROOT_DIRNAME}/first-dir - rm -rf first-dir - ;; + cd first-dir + mkdir sdir1 sdir2 + dotest basicb-2 "${testcvs} add sdir1 sdir2" \ +"Directory ${TESTDIR}/cvsroot/first-dir/sdir1 added to the repository +Directory ${TESTDIR}/cvsroot/first-dir/sdir2 added to the repository" + cd sdir1 + echo sfile1 starts >sfile1 + dotest basicb-2a10 "${testcvs} -n add sfile1" \ +"${PROG} [a-z]*: scheduling file .sfile1. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest basicb-2a11 "${testcvs} status sfile1" \ +"${PROG} [a-z]*: use .cvs add' to create an entry for sfile1 +=================================================================== +File: sfile1 Status: Unknown + + Working revision: No entry for sfile1 + Repository revision: No revision control file" + dotest basicb-3 "${testcvs} add sfile1" \ +"${PROG} [a-z]*: scheduling file .sfile1. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest basicb-3a1 "${testcvs} status sfile1" \ +"=================================================================== +File: sfile1 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + + cd ../sdir2 + echo sfile2 starts >sfile2 + dotest basicb-4 "${testcvs} add sfile2" \ +"${PROG} [a-z]*: scheduling file .sfile2. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest basicb-4a "${testcvs} -q ci CVS" \ +"${PROG} [a-z]*: warning: directory CVS specified in argument +${PROG} [a-z]*: but CVS uses CVS for its own purposes; skipping CVS directory" + cd .. + dotest basicb-5 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/sdir1/sfile1,v +done +Checking in sdir1/sfile1; +${TESTDIR}/cvsroot/first-dir/sdir1/sfile1,v <-- sfile1 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/sdir2/sfile2,v +done +Checking in sdir2/sfile2; +${TESTDIR}/cvsroot/first-dir/sdir2/sfile2,v <-- sfile2 +initial revision: 1\.1 +done" + echo sfile1 develops >sdir1/sfile1 + dotest basicb-6 "${testcvs} -q ci -m modify" \ +"Checking in sdir1/sfile1; +${TESTDIR}/cvsroot/first-dir/sdir1/sfile1,v <-- sfile1 +new revision: 1\.2; previous revision: 1\.1 +done" + dotest basicb-7 "${testcvs} -q tag release-1" 'T sdir1/sfile1 +T sdir2/sfile2' + echo not in time for release-1 >sdir2/sfile2 + dotest basicb-8 "${testcvs} -q ci -m modify-2" \ +"Checking in sdir2/sfile2; +${TESTDIR}/cvsroot/first-dir/sdir2/sfile2,v <-- sfile2 +new revision: 1\.2; previous revision: 1\.1 +done" + # See if CVS can correctly notice when an invalid numeric + # revision is specified. + # Commented out until we get around to fixing CVS +: dotest basicb-8a0 "${testcvs} diff -r 1.5 -r 1.7 sfile2" 'error msg' + cd .. + + # Test that we recurse into the correct directory when checking + # for existing files, even if co -d is in use. + touch first-dir/extra + dotest basicb-cod-1 "${testcvs} -q co -d first-dir1 first-dir" \ +'U first-dir1/sdir1/sfile1 +U first-dir1/sdir2/sfile2' + rm -rf first-dir1 + + rm -rf first-dir + dotest basicb-9 \ +"${testcvs} -q co -d newdir -r release-1 first-dir/sdir1 first-dir/sdir2" \ +'U newdir/sdir1/sfile1 +U newdir/sdir2/sfile2' + dotest basicb-9a "test -d CVS" '' + # See comment at modules3-7f for more on this behavior. + dotest basicb-9b "cat CVS/Repository" \ +"${TESTDIR}/cvsroot/first-dir" "${TESTDIR}/cvsroot/\." + dotest basicb-9c "cat newdir/CVS/Repository" \ +"${TESTDIR}/cvsroot/CVSROOT/Emptydir" + dotest basicb-10 "cat newdir/sdir1/sfile1 newdir/sdir2/sfile2" \ +"sfile1 develops +sfile2 starts" + + rm -rf newdir + + # Hmm, this might be a case for CVSNULLREPOS, but CVS doesn't + # seem to deal with it... + if false; then + dotest basicb-11 "${testcvs} -q co -d sub1/sub2 first-dir" \ +"U sub1/sub2/sdir1/sfile1 +U sub1/sub2/sdir2/sfile2" + cd sub1 + dotest basicb-12 "${testcvs} -q update" '' + touch xx + dotest basicb-13 "${testcvs} add xx" fixme + cd .. + rm -r sub1 + # to test: sub1/sub2/sub3 + fi # end of tests commented out. + + # Create a second directory. + mkdir 1 + cd 1 + dotest basicb-14 "${testcvs} -q co -l ." 'U topfile' + mkdir second-dir + dotest basicb-15 "${testcvs} add second-dir" \ +"Directory ${TESTDIR}/cvsroot/second-dir added to the repository" + cd second-dir + touch aa + dotest basicb-16 "${testcvs} add aa" \ +"${PROG} [a-z]*: scheduling file .aa. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest basicb-17 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/second-dir/aa,v +done +Checking in aa; +${TESTDIR}/cvsroot/second-dir/aa,v <-- aa +initial revision: 1\.1 +done" + cd ../.. + rm -rf 1 + # Now here is the kicker: note that the semantics of -d + # are fundamentally different if we specify two or more directories + # rather than one! I consider this to be seriously bogus, + # but for the moment I am just trying to figure out what + # CVS's current behaviors are. + dotest basicb-18 "${testcvs} -q co -d test2 first-dir second-dir" \ +"U test2/first-dir/sdir1/sfile1 +U test2/first-dir/sdir2/sfile2 +U test2/second-dir/aa" + cd test2 + touch emptyfile + # The fact that CVS lets us add a file here is a CVS bug, right? + # I can just make this an error message (on the add and/or the + # commit) without getting flamed, right? + # Right? + # Right? + dotest basicb-19 "${testcvs} add emptyfile" \ +"${PROG} [a-z]*: scheduling file .emptyfile. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest basicb-20 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/CVSROOT/Emptydir/emptyfile,v +done +Checking in emptyfile; +${TESTDIR}/cvsroot/CVSROOT/Emptydir/emptyfile,v <-- emptyfile +initial revision: 1\.1 +done" + cd .. + + mkdir 1; cd 1 + # "cvs admin" tests are scattered around a bit. Here we test + # ability to reject an unrecognized option. The "keyword" + # test has a test of "cvs admin -l" and the "binfiles" test + # has a test of "cvs admin -k". Note that -H is an illegal + # option. It probably should be an error message. But + # currently it is one error message for each file operated on, + # which in this case is zero files. + dotest basicb-21 "${testcvs} -q admin -H" "" + cd .. + rmdir 1 + + rm -rf test2 + + rm -rf ${CVSROOT_DIRNAME}/first-dir + rm -rf ${CVSROOT_DIRNAME}/second-dir + rm -f ${CVSROOT_DIRNAME}/topfile,v + ;; + + basic1) + # first dive - add a files, first singly, then in a group. + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1; cd 1 + # check out an empty directory + dotest basic1-1 "${testcvs} -q co first-dir" '' + + cd first-dir + echo file2 >file2 + echo file3 >file3 + echo file4 >file4 + echo file5 >file5 + + dotest basic1-14-add-add "${testcvs} add file2 file3 file4 file5" \ +"${PROG} [a-z]*: scheduling file \`file2' for addition +${PROG} [a-z]*: scheduling file \`file3' for addition +${PROG} [a-z]*: scheduling file \`file4' for addition +${PROG} [a-z]*: scheduling file \`file5' for addition +${PROG} [a-z]*: use 'cvs commit' to add these files permanently" + dotest basic1-15-add-add \ +"${testcvs} -q update file2 file3 file4 file5" \ +"A file2 +A file3 +A file4 +A file5" + dotest basic1-16-add-add "${testcvs} -q update" \ +"A file2 +A file3 +A file4 +A file5" + dotest basic1-17-add-add "${testcvs} -q status" \ +"=================================================================== +File: file2 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file3 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file4 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file5 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + dotest basic1-18-add-add "${testcvs} -q log" \ +"${PROG} [a-z]*: file2 has been added, but not committed +${PROG} [a-z]*: file3 has been added, but not committed +${PROG} [a-z]*: file4 has been added, but not committed +${PROG} [a-z]*: file5 has been added, but not committed" + cd .. + dotest basic1-21-add-add "${testcvs} -q update" \ +"A first-dir/file2 +A first-dir/file3 +A first-dir/file4 +A first-dir/file5" + # FIXCVS? Shouldn't this read first-dir/file2 instead of file2? + dotest basic1-22-add-add "${testcvs} log first-dir" \ +"${PROG} [a-z]*: Logging first-dir +${PROG} [a-z]*: file2 has been added, but not committed +${PROG} [a-z]*: file3 has been added, but not committed +${PROG} [a-z]*: file4 has been added, but not committed +${PROG} [a-z]*: file5 has been added, but not committed" + dotest basic1-23-add-add "${testcvs} status first-dir" \ +"${PROG} [a-z]*: Examining first-dir +=================================================================== +File: file2 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file3 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file4 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file5 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + dotest basic1-24-add-add "${testcvs} update first-dir" \ +"${PROG} [a-z]*: Updating first-dir +A first-dir/file2 +A first-dir/file3 +A first-dir/file4 +A first-dir/file5" + dotest basic1-27-add-add "${testcvs} co first-dir" \ +"${PROG} [a-z]*: Updating first-dir +A first-dir/file2 +A first-dir/file3 +A first-dir/file4 +A first-dir/file5" + cd first-dir + dotest basic1-14-add-ci \ +"${testcvs} commit -m test file2 file3 file4 file5" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file3,v +done +Checking in file3; +${TESTDIR}/cvsroot/first-dir/file3,v <-- file3 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file4,v +done +Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file5,v +done +Checking in file5; +${TESTDIR}/cvsroot/first-dir/file5,v <-- file5 +initial revision: 1\.1 +done" + dotest basic1-15-add-ci \ +"${testcvs} -q update file2 file3 file4 file5" '' + dotest basic1-16-add-ci "${testcvs} -q update" '' + dotest basic1-17-add-ci "${testcvs} -q status" \ +"=================================================================== +File: file2 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file2,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file3 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file3,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file4 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file4,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file5 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file5,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + # The "log" tests and friends probably already test the output + # from log quite adequately. + # Note: using dotest fails here. It seems to be related + # to the output being sufficiently large (Red Hat 4.1). + # dotest basic1-18-add-ci "${testcvs} log" "${DOTSTAR}" + if ${testcvs} -q log >>${LOGFILE}; then + pass basic1-18-add-ci + else + pass basic1-18-add-ci + fi + cd .. + dotest basic1-21-add-ci "${testcvs} -q update" '' + # See test basic1-18-add-ci for explanation of non-use of dotest. + if ${testcvs} -q log first-dir >>${LOGFILE}; then + pass basic1-22-add-ci + else + pass basic1-22-add-ci + fi + # At least for the moment I am going to consider 17-add-ci + # an adequate test of the output here. + # See test basic1-18-add-ci for explanation of non-use of dotest. + if ${testcvs} -q status first-dir >>${LOGFILE}; then + pass basic1-23-add-ci + else + pass basic1-23-add-ci + fi + dotest basic1-24-add-ci "${testcvs} -q update first-dir" '' + dotest basic1-27-add-ci "${testcvs} -q co first-dir" '' + + cd first-dir + rm file2 file3 file4 file5 + dotest basic1-14-rm-rm "${testcvs} rm file2 file3 file4 file5" \ +"${PROG} [a-z]*: scheduling .file2. for removal +${PROG} [a-z]*: scheduling .file3. for removal +${PROG} [a-z]*: scheduling .file4. for removal +${PROG} [a-z]*: scheduling .file5. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove these files permanently" + # 15-rm-rm was commented out. Why? + dotest basic1-15-rm-rm \ +"${testcvs} -q update file2 file3 file4 file5" \ +"R file2 +R file3 +R file4 +R file5" + dotest basic1-16-rm-rm "${testcvs} -q update" \ +"R file2 +R file3 +R file4 +R file5" + dotest basic1-17-rm-rm "${testcvs} -q status" \ +"=================================================================== +File: no file file2 Status: Locally Removed + + Working revision: -1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file2,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: no file file3 Status: Locally Removed + + Working revision: -1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file3,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: no file file4 Status: Locally Removed + + Working revision: -1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file4,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: no file file5 Status: Locally Removed + + Working revision: -1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file5,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + # Would be nice to test that real logs appear (with dead state + # and all), either here or someplace like log2 tests. + if ${testcvs} -q log >>${LOGFILE}; then + pass basic1-18-rm-rm + else + fail basic1-18-rm-rm + fi + cd .. + dotest basic1-21-rm-rm "${testcvs} -q update" \ +"R first-dir/file2 +R first-dir/file3 +R first-dir/file4 +R first-dir/file5" + if ${testcvs} -q log first-dir >>${LOGFILE}; then + pass basic1-22-rm-rm + else + fail basic1-22-rm-rm + fi + if ${testcvs} -q status first-dir >>${LOGFILE}; then + pass basic1-23-rm-rm + else + fail basic1-23-rm-rm + fi + dotest basic1-24-rm-rm "${testcvs} -q update first-dir" \ +"R first-dir/file2 +R first-dir/file3 +R first-dir/file4 +R first-dir/file5" + dotest basic1-27-rm-rm "${testcvs} -q co first-dir" \ +"R first-dir/file2 +R first-dir/file3 +R first-dir/file4 +R first-dir/file5" + cd first-dir + dotest basic1-14-rm-ci "${testcvs} -q commit -m test" \ +"Removing file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: delete; previous revision: 1\.1 +done +Removing file3; +${TESTDIR}/cvsroot/first-dir/file3,v <-- file3 +new revision: delete; previous revision: 1\.1 +done +Removing file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: delete; previous revision: 1\.1 +done +Removing file5; +${TESTDIR}/cvsroot/first-dir/file5,v <-- file5 +new revision: delete; previous revision: 1\.1 +done" + dotest basic1-15-rm-ci \ +"${testcvs} -q update file2 file3 file4 file5" '' + dotest basic1-16-rm-ci "${testcvs} -q update" '' + dotest basic1-17-rm-ci "${testcvs} -q status" '' + # Would be nice to test that real logs appear (with dead state + # and all), either here or someplace like log2 tests. + if ${testcvs} -q log >>${LOGFILE}; then + pass basic1-18-rm-ci + else + fail basic1-18-rm-ci + fi + cd .. + dotest basic1-21-rm-ci "${testcvs} -q update" '' + if ${testcvs} -q log first-dir >>${LOGFILE}; then + pass basic1-22-rm-ci + else + fail basic1-22-rm-ci + fi + if ${testcvs} -q status first-dir >>${LOGFILE}; then + pass basic1-23-rm-ci + else + fail basic1-23-rm-ci + fi + dotest basic1-24-rm-ci "${testcvs} -q update first-dir" '' + dotest basic1-27-rm-ci "${testcvs} -q co first-dir" '' + cd first-dir + # All the files are removed, so nothing gets tagged. + dotest basic1-28 "${testcvs} -q tag first-dive" '' + cd .. + cd .. + + if test "$keep" = yes; then + echo Keeping ${TESTDIR} and exiting due to --keep + exit 0 + fi + + rm -rf 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; deep) # Test the ability to operate on directories nested rather deeply. @@ -631,7 +1304,7 @@ ${PROG}"': does not match command line -d /tmp/cvs-sanity/nonexist setting for i in dir1 dir2 dir3 dir4 dir5 dir6 dir7 dir8; do mkdir $i dotest deep-2-$i "${testcvs} add $i" \ -'Directory /tmp/cvs-sanity/cvsroot/first-dir/dir1[/dir0-9]* added to the repository' +"Directory ${TESTDIR}/cvsroot/first-dir/dir1[/dir0-9]* added to the repository" cd $i echo file1 >file1 dotest deep-3-$i "${testcvs} add file1" \ @@ -639,55 +1312,121 @@ ${PROG}"': does not match command line -d /tmp/cvs-sanity/nonexist setting '"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' done cd ../../../../../../../../.. - dotest deep-4 "${testcvs} -q ci -m add-them first-dir" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/file1,v + dotest_lit deep-4 "${testcvs} -q ci -m add-them first-dir" <<HERE +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/file1,v done Checking in first-dir/dir1/file1; -/tmp/cvs-sanity/cvsroot/first-dir/dir1/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/dir1/file1,v <-- file1 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/file1,v +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/dir2/file1,v done Checking in first-dir/dir1/dir2/file1; -/tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/dir1/dir2/file1,v <-- file1 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/file1,v +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/file1,v done Checking in first-dir/dir1/dir2/dir3/file1; -/tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/file1,v <-- file1 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/file1,v +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/file1,v done Checking in first-dir/dir1/dir2/dir3/dir4/file1; -/tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/file1,v <-- file1 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/file1,v +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/file1,v done Checking in first-dir/dir1/dir2/dir3/dir4/dir5/file1; -/tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/file1,v <-- file1 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1,v +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1,v done Checking in first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1; -/tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1,v <-- file1 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1,v +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1,v done Checking in first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1; -/tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1,v <-- file1 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v done Checking in first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1; -/tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v <-- file1 initial revision: 1.1 -done' +done +HERE + + cd first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8 + rm file1 + dotest deep-4a0 "${testcvs} rm file1" \ +"${PROG} [a-z]*: scheduling .file1. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + dotest deep-4a1 "${testcvs} -q ci -m rm-it" "Removing file1; +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v <-- file1 +new revision: delete; previous revision: 1\.1 +done" + cd ../../.. + dotest deep-4a2 "${testcvs} -q update -P dir6/dir7" '' + # Should be using "test -e" if that is portable enough. + dotest_fail deep-4a3 "test -d dir6/dir7/dir8" '' + + # Test that if we remove the working directory, CVS does not + # recreate it. (I realize that this behavior is what the + # users expect, but in the longer run we might want to + # re-think it. The corresponding behavior for a file is that + # CVS *will* recreate it, and we might want to make it so + # that "cvs release -d" is the way to delete the directory + # and have it stay gone -kingdon, Oct1996). + rm -rf dir6 + dotest deep-4b1 "${testcvs} -q update" '' + dotest deep-4b2 "${testcvs} -q update -d -P" \ +'U dir6/file1 +U dir6/dir7/file1' + + # Test what happens if one uses -P when there are files removed + # but not committed. + cd dir6/dir7 + dotest deep-rm1 "${testcvs} rm -f file1" \ +"${PROG} [a-z]*: scheduling .file1. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + cd .. + dotest deep-rm2 "${testcvs} -q update -d -P" 'R dir7/file1' + dotest deep-rm3 "test -d dir7" '' + dotest deep-rm4 "${testcvs} -q ci -m rm-it" "Removing dir7/file1; +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1,v <-- file1 +new revision: delete; previous revision: 1\.1 +done" + dotest deep-rm5 "${testcvs} -q update -d -P" '' + dotest_fail deep-rm6 "test -d dir7" '' + + # Test rm -f -R. + cd ../.. + dotest deep-rm7 "${testcvs} rm -f -R dir5" \ +"${PROG} [a-z]*: Removing dir5 +${PROG} [a-z]*: scheduling .dir5/file1. for removal +${PROG} [a-z]*: Removing dir5/dir6 +${PROG} [a-z]*: scheduling .dir5/dir6/file1. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove these files permanently" + dotest deep-rm8 "${testcvs} -q ci -m rm-it" \ +"Removing dir5/file1; +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/file1,v <-- file1 +new revision: delete; previous revision: 1\.1 +done +Removing dir5/dir6/file1; +${TESTDIR}/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1,v <-- file1 +new revision: delete; previous revision: 1\.1 +done" + dotest deep-rm9 "${testcvs} -q update -d -P" '' + dotest_fail deep-rm10 "test -d dir5" + + cd ../../../../.. if echo "yes" | ${testcvs} release -d first-dir >>${LOGFILE}; then pass deep-5 @@ -700,14 +1439,21 @@ done' basic2) # Test rtag, import, history, various miscellaneous operations + # NOTE: this section has reached the size and + # complexity where it is getting to be a good idea to + # add new tests to a new section rather than + # continuing to piggyback them onto the tests here. + # First empty the history file rm ${CVSROOT_DIRNAME}/CVSROOT/history touch ${CVSROOT_DIRNAME}/CVSROOT/history +### XXX maybe should use 'cvs imprt -b1 -m new-module first-dir F F1' in an +### empty directory to do this instead of hacking directly into $CVSROOT mkdir ${CVSROOT_DIRNAME}/first-dir dotest basic2-1 "${testcvs} -q co first-dir" '' for i in first-dir dir1 dir2 ; do - if [ ! -d $i ] ; then + if test ! -d $i ; then mkdir $i if ${CVS} add $i >> ${LOGFILE}; then echo "PASS: test 29-$i" >>${LOGFILE} @@ -748,7 +1494,8 @@ done' echo "FAIL: test 33" | tee -a ${LOGFILE} ; exit 1 fi -# if ${CVS} diff -u first-dir >> ${LOGFILE} || [ $? = 1 ] ; then +# XXX why is this commented out??? +# if ${CVS} diff -u first-dir >> ${LOGFILE} || test $? = 1 ; then # echo "PASS: test 34" >>${LOGFILE} # else # echo "FAIL: test 34" | tee -a ${LOGFILE} # ; exit 1 @@ -800,7 +1547,7 @@ done' echo "FAIL: test 39" | tee -a ${LOGFILE} ; exit 1 fi - # fixme: doesn't work right for added files + # FIXME: doesn't work right for added files if ${CVS} log first-dir >> ${LOGFILE}; then echo "PASS: test 40" >>${LOGFILE} else @@ -813,7 +1560,8 @@ done' echo "FAIL: test 41" | tee -a ${LOGFILE} ; exit 1 fi -# if ${CVS} diff -u first-dir >> ${LOGFILE} || [ $? = 1 ] ; then +# XXX why is this commented out? +# if ${CVS} diff -u first-dir >> ${LOGFILE} || test $? = 1 ; then # echo "PASS: test 42" >>${LOGFILE} # else # echo "FAIL: test 42" | tee -a ${LOGFILE} # ; exit 1 @@ -839,7 +1587,7 @@ done' fi # end of third dive - if [ -d first-dir ] ; then + if test -d first-dir ; then echo "FAIL: test 45.5" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 45.5" >>${LOGFILE} @@ -869,7 +1617,7 @@ done' fi # rdiff by revision - if ${CVS} rdiff -r1.1 -rrtagged-by-head first-dir >> ${LOGFILE} || [ $? = 1 ] ; then + if ${CVS} rdiff -r1.1 -rrtagged-by-head first-dir >> ${LOGFILE} || test $? = 1 ; then echo "PASS: test 49" >>${LOGFILE} else echo "FAIL: test 49" | tee -a ${LOGFILE} ; exit 1 @@ -913,33 +1661,33 @@ done' fi # directory copies are done in an oblique way in order to avoid a bug in sun's tmp filesystem. - mkdir first-dir.cpy ; (cd first-dir ; tar cf - * | (cd ../first-dir.cpy ; tar xf -)) + mkdir first-dir.cpy ; (cd first-dir ; tar cf - . | (cd ../first-dir.cpy ; tar xf -)) directory_cmp first-dir export-dir - if $ISDIFF ; then + if $ISDIFF ; then echo "FAIL: test 55" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 55" >>${LOGFILE} fi - # interrupt, while we've got a clean 1.1 here, let's import it into another tree. + # interrupt, while we've got a clean 1.1 here, let's import it + # into a couple of other modules. cd export-dir dotest 56 "${testcvs} import -m first-import second-dir first-immigration immigration1 immigration1_0" \ -'N second-dir/file14 +"N second-dir/file14 N second-dir/file6 N second-dir/file7 -'"${PROG}"' [a-z]*: Importing /tmp/cvs-sanity/cvsroot/second-dir/dir1 +${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/dir1 N second-dir/dir1/file14 N second-dir/dir1/file6 N second-dir/dir1/file7 -'"${PROG}"' [a-z]*: Importing /tmp/cvs-sanity/cvsroot/second-dir/dir1/dir2 +${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/dir1/dir2 N second-dir/dir1/dir2/file14 N second-dir/dir1/dir2/file6 N second-dir/dir1/dir2/file7 -No conflicts created by this import' - +No conflicts created by this import" cd .. if ${CVS} export -r HEAD second-dir ; then @@ -957,9 +1705,10 @@ No conflicts created by this import' fi rm -rf second-dir + rm -rf export-dir first-dir mkdir first-dir - (cd first-dir.cpy ; tar cf - * | (cd ../first-dir ; tar xf -)) + (cd first-dir.cpy ; tar cf - . | (cd ../first-dir ; tar xf -)) # update the top, cancelling sticky tags, retag, update other copy, compare. cd first-dir @@ -1011,7 +1760,7 @@ No conflicts created by this import' # Test the cvs history command. # The reason that there are two patterns rather than using - # \(/tmp/cvs-sanity\|<remote>\) is that we are trying to + # \(${TESTDIR}\|<remote>\) is that we are trying to # make this portable. Perhaps at some point we should # ditch that notion and require GNU expr (or dejagnu or....) # since it seems to be so painful. @@ -1019,51 +1768,163 @@ No conflicts created by this import' # why are there two lines at the end of the local output # which don't exist in the remote output? would seem to be # a CVS bug. - dotest basic2-64 "${testcvs} his -e -a" \ -'O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir =first-dir= /tmp/cvs-sanity/\* -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir/dir1 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir/dir1 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir/dir1/dir2 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir/dir1/dir2 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir == /tmp/cvs-sanity -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir/dir1 == /tmp/cvs-sanity -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir/dir1 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir/dir1/dir2 == /tmp/cvs-sanity -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir/dir1/dir2 == /tmp/cvs-sanity -F [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* =first-dir= /tmp/cvs-sanity/\* -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-head:A\] -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-tag:rtagged-by-head\] -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-revision:1.1\] -O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* \[1.1\] first-dir =first-dir= /tmp/cvs-sanity/\* -U [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir == /tmp/cvs-sanity/first-dir -U [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file7 first-dir == /tmp/cvs-sanity/first-dir' \ -'O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir =first-dir= <remote>/\* -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir/dir1/dir2 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir/dir1/dir2 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir == <remote> -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir/dir1 == <remote> -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir/dir1/dir2 == <remote> -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir/dir1/dir2 == <remote> -F [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* =first-dir= <remote>/\* -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-head:A\] -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-tag:rtagged-by-head\] -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-revision:1.1\] -O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* \[1.1\] first-dir =first-dir= <remote>/\*' + dotest basic2-64 "${testcvs} his -x TOFWUCGMAR -a" \ +"O [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* first-dir =first-dir= ${TMPPWD}/\* +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir == ${TMPPWD} +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir == ${TMPPWD} +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir/dir1 == ${TMPPWD} +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir/dir1 == ${TMPPWD} +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir/dir1/dir2 == ${TMPPWD} +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir/dir1/dir2 == ${TMPPWD} +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir == ${TMPPWD} +M [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir == ${TMPPWD} +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir/dir1 == ${TMPPWD} +M [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir/dir1 == ${TMPPWD} +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir/dir1/dir2 == ${TMPPWD} +M [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir/dir1/dir2 == ${TMPPWD} +F [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* =first-dir= ${TMPPWD}/\* +T [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-head:A\] +T [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-tag:rtagged-by-head\] +T [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-revision:1\.1\] +O [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* \[1\.1\] first-dir =first-dir= ${TMPPWD}/\* +U [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir == ${TMPPWD}/first-dir +U [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.2 file7 first-dir == ${TMPPWD}/first-dir" \ +"O [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* first-dir =first-dir= <remote>/\* +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir == <remote> +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir == <remote> +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir/dir1 == <remote> +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir/dir1 == <remote> +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir/dir1/dir2 == <remote> +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir/dir1/dir2 == <remote> +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir == <remote> +M [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir == <remote> +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir/dir1 == <remote> +M [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir/dir1 == <remote> +A [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir/dir1/dir2 == <remote> +M [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir/dir1/dir2 == <remote> +F [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* =first-dir= <remote>/\* +T [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-head:A\] +T [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-tag:rtagged-by-head\] +T [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-revision:1\.1\] +O [0-9/]* [0-9:]* ${PLUS}0000 [a-z0-9@][a-z0-9@]* \[1\.1\] first-dir =first-dir= <remote>/\*" rm -rf ${CVSROOT_DIRNAME}/first-dir rm -rf ${CVSROOT_DIRNAME}/second-dir ;; - death) # next dive. test death support. + rdiff) + # Test rdiff + # XXX for now this is just the most essential test... + cd ${TESTDIR} + + mkdir testimport + cd testimport + echo '$''Id$' > foo + echo '$''Name$' >> foo + echo '$''Id$' > bar + echo '$''Name$' >> bar + dotest rdiff-1 \ + "${testcvs} import -I ! -m test-import-with-keyword trdiff TRDIFF T1" \ +'N trdiff/foo +N trdiff/bar + +No conflicts created by this import' + dotest rdiff-2 \ + "${testcvs} co -ko trdiff" \ +"${PROG} [a-z]*: Updating trdiff +U trdiff/bar +U trdiff/foo" + cd trdiff + echo something >> foo + dotest rdiff-3 \ + "${testcvs} ci -m added-something foo" \ +"Checking in foo; +${TESTDIR}/cvsroot/trdiff/foo,v <-- foo +new revision: 1\.2; previous revision: 1\.1 +done" + echo '#ident "@(#)trdiff:$''Name$:$''Id$"' > new + echo "new file" >> new + dotest rdiff-4 \ + "${testcvs} add -m new-file-description new" \ +"${PROG} [a-z]*: scheduling file \`new' for addition +${PROG} [a-z]*: use 'cvs commit' to add this file permanently" + dotest rdiff-5 \ + "${testcvs} commit -m added-new-file new" \ +"RCS file: ${TESTDIR}/cvsroot/trdiff/new,v +done +Checking in new; +${TESTDIR}/cvsroot/trdiff/new,v <-- new +initial revision: 1\.1 +done" + dotest rdiff-6 \ + "${testcvs} tag local-v0" \ +"${PROG} [a-z]*: Tagging . +T bar +T foo +T new" + dotest rdiff-7 \ + "${testcvs} status -v foo" \ +"=================================================================== +File: foo Status: Up-to-date + + Working revision: 1\.2.* + Repository revision: 1\.2 ${TESTDIR}/cvsroot/trdiff/foo,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -ko + + Existing Tags: + local-v0 (revision: 1\.2) + T1 (revision: 1\.1\.1\.1) + TRDIFF (branch: 1\.1\.1)" + + cd .. + rm -rf trdiff + + dotest rdiff-8 \ + "${testcvs} rdiff -r T1 -r local-v0 trdiff" \ +"${PROG}"' [a-z]*: Diffing trdiff +Index: trdiff/foo +diff -c trdiff/foo:1\.1\.1\.1 trdiff/foo:1\.2 +\*\*\* trdiff/foo:1\.1\.1\.1 .* +--- trdiff/foo .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1,2 \*\*\*\* +! \$''Id: foo,v 1\.1\.1\.1 [0-9/]* [0-9:]* '"${username}"' Exp \$ +! \$''Name: T1 \$ +--- 1,3 ---- +! \$''Id: foo,v 1\.2 [0-9/]* [0-9:]* '"${username}"' Exp \$ +! \$''Name: local-v0 \$ +! something +Index: trdiff/new +diff -c /dev/null trdiff/new:1\.1 +\*\*\* /dev/null .* +--- trdiff/new .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1,2 ---- +'"${PLUS}"' #ident "@(#)trdiff:\$''Name: local-v0 \$:\$''Id: new,v 1\.1 [0-9/]* [0-9:]* '"${username}"' Exp \$" +'"${PLUS}"' new file' + +# FIXME: will this work here? +# if test "$keep" = yes; then +# echo Keeping ${TESTDIR} and exiting due to --keep +# exit 0 +# fi + + cd .. + rm -rf testimport + rm -rf ${CVSROOT_DIRNAME}/trdiff + ;; + + death) + # next dive. test death support. + + # NOTE: this section has reached the size and + # complexity where it is getting to be a good idea to + # add new death support tests to a new section rather + # than continuing to piggyback them onto the tests here. + mkdir ${CVSROOT_DIRNAME}/first-dir if ${CVS} co first-dir ; then echo "PASS: test 65" >>${LOGFILE} @@ -1077,28 +1938,28 @@ O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* \[1.1\] first-dir =fir # doesn't get confused by it. mkdir subdir dotest 65a0 "${testcvs} add subdir" \ -'Directory /tmp/cvs-sanity/cvsroot/first-dir/subdir added to the repository' +"Directory ${TESTDIR}/cvsroot/first-dir/subdir added to the repository" cd subdir echo file in subdir >sfile dotest 65a1 "${testcvs} add sfile" \ "${PROG}"' [a-z]*: scheduling file `sfile'\'' for addition '"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' dotest 65a2 "${testcvs} -q ci -m add-it" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/subdir/sfile,v +"RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/sfile,v done Checking in sfile; -/tmp/cvs-sanity/cvsroot/first-dir/subdir/sfile,v <-- sfile -initial revision: 1.1 -done' +${TESTDIR}/cvsroot/first-dir/subdir/sfile,v <-- sfile +initial revision: 1\.1 +done" rm sfile dotest 65a3 "${testcvs} rm sfile" \ "${PROG}"' [a-z]*: scheduling `sfile'\'' for removal '"${PROG}"' [a-z]*: use '\'"${PROG}"' commit'\'' to remove this file permanently' dotest 65a4 "${testcvs} -q ci -m remove-it" \ -'Removing sfile; -/tmp/cvs-sanity/cvsroot/first-dir/subdir/sfile,v <-- sfile -new revision: delete; previous revision: 1.1 -done' +"Removing sfile; +${TESTDIR}/cvsroot/first-dir/subdir/sfile,v <-- sfile +new revision: delete; previous revision: 1\.1 +done" cd .. dotest 65a5 "${testcvs} -q update -P" '' dotest_fail 65a6 "test -d subdir" '' @@ -1170,21 +2031,25 @@ done' "${PROG}"' [a-z]*: scheduling file `file4'\'' for addition '"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' dotest death-file4-ciadd "${testcvs} -q ci -m add file4" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v +"RCS file: ${TESTDIR}/cvsroot/first-dir/file4,v done Checking in file4; -/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -initial revision: 1.1 -done' +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +initial revision: 1\.1 +done" rm file4 dotest death-file4-rm "${testcvs} remove file4" \ "${PROG}"' [a-z]*: scheduling `file4'\'' for removal '"${PROG}"' [a-z]*: use '\'"${PROG}"' commit'\'' to remove this file permanently' dotest death-file4-cirm "${testcvs} -q ci -m remove file4" \ -'Removing file4; -/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: delete; previous revision: 1.1 -done' +"Removing file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: delete; previous revision: 1\.1 +done" + + # Tag the branchpoint. + dotest death-72a "${testcvs} -q tag bp_branch1" 'T file1 +T file2' # branch1 if ${CVS} tag -b branch1 ; then @@ -1217,6 +2082,27 @@ done' echo "FAIL: test 76" | tee -a ${LOGFILE} ; exit 1 fi + dotest death-76a0 \ +"${testcvs} -q rdiff -r bp_branch1 -r branch1 first-dir" \ +"Index: first-dir/file3 +diff -c /dev/null first-dir/file3:1\.1\.2\.1 +\*\*\* /dev/null .* +--- first-dir/file3 .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} line1 from branch1" + dotest death-76a1 \ +"${testcvs} -q rdiff -r branch1 -r bp_branch1 first-dir" \ +'Index: first-dir/file3 +diff -c first-dir/file3:1\.1\.2\.1 first-dir/file3:removed +\*\*\* first-dir/file3:1\.1\.2\.1 .* +--- first-dir/file3 .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- line1 from branch1 +--- 0 ----' + # remove rm file3 if ${CVS} rm file3 2>> ${LOGFILE}; then @@ -1281,22 +2167,24 @@ done' dotest_fail death-file4-4 "test -f file4" '' - if [ -f file3 ] ; then + if test -f file3 ; then echo "FAIL: test 85" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 85" >>${LOGFILE} fi # join - if ${CVS} update -j branch1 >> ${LOGFILE} 2>&1; then - echo "PASS: test 86" >>${LOGFILE} - else - echo "FAIL: test 86" | tee -a ${LOGFILE} ; exit 1 - fi + dotest 86 "${testcvs} -q update -j branch1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +retrieving revision 1\.3 +retrieving revision 1\.3\.2\.1 +Merging differences between 1\.3 and 1\.3\.2\.1 into file1 +${PROG} [a-z]*: scheduling file2 for removal +U file3" dotest_fail death-file4-5 "test -f file4" '' - if [ -f file3 ] ; then + if test -f file3 ; then echo "PASS: test 87" >>${LOGFILE} else echo "FAIL: test 87" | tee -a ${LOGFILE} ; exit 1 @@ -1318,11 +2206,27 @@ done' fi # commit - if ${CVS} ci -m test >>${LOGFILE} 2>&1; then - echo "PASS: test 89" >>${LOGFILE} - else - echo "FAIL: test 89" | tee -a ${LOGFILE} ; exit 1 - fi + dotest 89 "${testcvs} -q ci -m test" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.4; previous revision: 1\.3 +done +Removing file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: delete; previous revision: 1\.1 +done +Checking in file3; +${TESTDIR}/cvsroot/first-dir/file3,v <-- file3 +new revision: 1\.2; previous revision: 1\.1 +done" + cd .. + mkdir 2 + cd 2 + dotest 89a "${testcvs} -q co first-dir" 'U first-dir/file1 +U first-dir/file3' + cd .. + rm -rf 2 + cd first-dir # remove first file. rm file1 @@ -1339,7 +2243,7 @@ done' echo "FAIL: test 91" | tee -a ${LOGFILE} ; exit 1 fi - if [ -f file1 ] ; then + if test -f file1 ; then echo "FAIL: test 92" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 92" >>${LOGFILE} @@ -1354,7 +2258,7 @@ done' else echo "PASS: 92.1b" >>${LOGFILE} fi - if test -f file2 ; then + if test -f file3 ; then echo "PASS: 92.1c" >>${LOGFILE} else echo "FAIL: 92.1c" | tee -a ${LOGFILE} ; exit 1 @@ -1369,23 +2273,308 @@ done' dotest_fail death-file4-6 "test -f file4" '' - if [ -f file1 ] ; then + if test -f file1 ; then echo "PASS: test 94" >>${LOGFILE} else echo "FAIL: test 94" | tee -a ${LOGFILE} ; exit 1 fi # and join - if ${CVS} update -j HEAD >> ${LOGFILE} 2>&1; then - echo "PASS: test 95" >>${LOGFILE} - else - echo "FAIL: test 95" | tee -a ${LOGFILE} ; exit 1 - fi + dotest 95 "${testcvs} -q update -j HEAD" \ +"${PROG}"' [a-z]*: file file1 has been modified, but has been removed in revision HEAD +'"${PROG}"' [a-z]*: file file3 exists, but has been added in revision HEAD' dotest_fail death-file4-7 "test -f file4" '' + # file2 should not have been recreated. It was + # deleted on the branch, and has not been modified on + # the trunk. That means that there have been no + # changes between the greatest common ancestor (the + # trunk version) and HEAD. + dotest_fail death-file2-1 "test -f file2" '' + cd .. ; rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir ;; + + death2) + # More tests of death support. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest death2-1 "${testcvs} -q co first-dir" '' + + cd first-dir + + # Add a file on the trunk. + echo "first revision" > file1 + dotest death2-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + + dotest death2-3 "${testcvs} -q commit -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + + # Make a branch and a non-branch tag. + dotest death2-4 "${testcvs} -q tag -b branch" 'T file1' + dotest death2-5 "${testcvs} -q tag tag" 'T file1' + + # Switch over to the branch. + dotest death2-6 "${testcvs} -q update -r branch" '' + + # Delete the file on the branch. + rm file1 + dotest death2-7 "${testcvs} rm file1" \ +"${PROG} [a-z]*: scheduling .file1. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + + # Test diff of the removed file before it is committed. + dotest_fail death2-diff-1 "${testcvs} -q diff file1" \ +"${PROG} [a-z]*: file1 was removed, no comparison available" + + dotest_fail death2-diff-2 "${testcvs} -q diff -N -c file1" \ +"Index: file1 +=================================================================== +RCS file: file1 +diff -N file1 +\*\*\* ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +--- /dev/null[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- first revision +--- 0 ----" + + dotest death2-8 "${testcvs} -q ci -m removed" \ +"Removing file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: delete; previous revision: 1\.1\.2 +done" + + # Test diff of a dead file. + dotest_fail death2-diff-3 \ +"${testcvs} -q diff -r1.1 -rbranch -c file1" \ +"${PROG} [a-z]*: file1 was removed, no comparison available" + + dotest_fail death2-diff-4 \ +"${testcvs} -q diff -r1.1 -rbranch -N -c file1" \ +"Index: file1 +=================================================================== +RCS file: file1 +diff -N file1 +\*\*\* ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +--- /dev/null[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- first revision +--- 0 ----" + + dotest_fail death2-diff-5 "${testcvs} -q diff -rtag -c ." \ +"${PROG} [a-z]*: file1 no longer exists, no comparison available" + + dotest_fail death2-diff-6 "${testcvs} -q diff -rtag -N -c ." \ +"Index: file1 +=================================================================== +RCS file: file1 +diff -N file1 +\*\*\* ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +--- /dev/null[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- first revision +--- 0 ----" + + # Test rdiff of a dead file. + dotest death2-rdiff-1 \ +"${testcvs} -q rtag -rbranch rdiff-tag first-dir" '' + + dotest death2-rdiff-2 "${testcvs} -q rdiff -rtag -rbranch first-dir" \ +"Index: first-dir/file1 +diff -c first-dir/file1:1\.1 first-dir/file1:removed +\*\*\* first-dir/file1:1\.1[ ][ ]*[a-zA-Z0-9: ]* +--- first-dir/file1[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- first revision +--- 0 ----" + + # Readd the file to the branch. + echo "second revision" > file1 + dotest death2-9 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: file `file1'\'' will be added on branch `branch'\'' from version 1\.1\.2\.1 +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + + # Test diff of the added file before it is committed. + dotest_fail death2-diff-7 "${testcvs} -q diff file1" \ +"${PROG} [a-z]*: file1 is a new entry, no comparison available" + + dotest_fail death2-diff-8 "${testcvs} -q diff -N -c file1" \ +"Index: file1 +=================================================================== +RCS file: file1 +diff -N file1 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} second revision" + + dotest death2-10 "${testcvs} -q commit -m add" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1 +done" + + # Back to the trunk. + dotest death2-11 "${testcvs} -q update -A" 'U file1' 'P file1' + + # Add another file on the trunk. + echo "first revision" > file2 + dotest death2-12 "${testcvs} add file2" \ +"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest death2-13 "${testcvs} -q commit -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done" + + # Back to the branch. + # The ``no longer in the repository'' message doesn't really + # look right to me, but that's what CVS currently prints for + # this case. + dotest death2-14 "${testcvs} -q update -r branch" \ +"U file1 +${PROG} [a-z]*: file2 is no longer in the repository" \ +"P file1 +${PROG} [a-z]*: file2 is no longer in the repository" + + # Add a file on the branch with the same name. + echo "branch revision" > file2 + dotest death2-15 "${testcvs} add file2" \ +"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest death2-16 "${testcvs} -q commit -m add" \ +"Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + + # Add a new file on the branch. + echo "first revision" > file3 + dotest death2-17 "${testcvs} add file3" \ +"${PROG}"' [a-z]*: scheduling file `file3'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest death2-18 "${testcvs} -q commit -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/file3,v +done +Checking in file3; +${TESTDIR}/cvsroot/first-dir/Attic/file3,v <-- file3 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + + # Test diff of a nonexistent tag + dotest_fail death2-diff-9 "${testcvs} -q diff -rtag -c file3" \ +"${PROG} [a-z]*: tag tag is not in file file3" + + dotest_fail death2-diff-10 "${testcvs} -q diff -rtag -N -c file3" \ +"Index: file3 +=================================================================== +RCS file: file3 +diff -N file3 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} first revision" + + dotest_fail death2-diff-11 "${testcvs} -q diff -rtag -c ." \ +"Index: file1 +=================================================================== +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +retrieving revision 1\.1 +retrieving revision 1\.1\.2\.2 +diff -c -r1\.1 -r1\.1\.2\.2 +\*\*\* file1[ ][ ]*[a-zA-Z0-9:./ ]* +--- file1[ ][ ]*[a-zA-Z0-9:./ ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +! first revision +--- 1 ---- +! second revision +${PROG} [a-z]*: tag tag is not in file file2 +${PROG} [a-z]*: tag tag is not in file file3" + + dotest_fail death2-diff-12 "${testcvs} -q diff -rtag -c -N ." \ +"Index: file1 +=================================================================== +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +retrieving revision 1\.1 +retrieving revision 1\.1\.2\.2 +diff -c -r1\.1 -r1\.1\.2\.2 +\*\*\* file1[ ][ ]*[a-zA-Z0-9:./ ]* +--- file1[ ][ ]*[a-zA-Z0-9:./ ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +! first revision +--- 1 ---- +! second revision +Index: file2 +=================================================================== +RCS file: file2 +diff -N file2 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} branch revision +Index: file3 +=================================================================== +RCS file: file3 +diff -N file3 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} first revision" + + # Switch to the nonbranch tag. + dotest death2-19 "${testcvs} -q update -r tag" \ +"U file1 +${PROG} [a-z]*: file2 is no longer in the repository +${PROG} [a-z]*: file3 is no longer in the repository" \ +"P file1 +${PROG} [a-z]*: file2 is no longer in the repository +${PROG} [a-z]*: file3 is no longer in the repository" + + dotest_fail death2-20 "test -f file2" + + # Make sure diff only reports appropriate files. + dotest_fail death2-diff-13 "${testcvs} -q diff -r rdiff-tag" \ +"${PROG} [a-z]*: file1 is a new entry, no comparison available" + + dotest_fail death2-diff-14 "${testcvs} -q diff -r rdiff-tag -c -N" \ +"Index: file1 +=================================================================== +RCS file: file1 +diff -N file1 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} first revision" + + cd .. ; rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir + ;; + branches) # More branch tests, including branches off of branches mkdir ${CVSROOT_DIRNAME}/first-dir @@ -1401,37 +2590,38 @@ done' '"${PROG}"' [a-z]*: scheduling file `file3'\'' for addition '"${PROG}"' [a-z]*: scheduling file `file4'\'' for addition '"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' - dotest branches-3 "${testcvs} -q ci -m add-it" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v + dotest_lit branches-3 "${testcvs} -q ci -m add-it" <<HERE +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done Checking in file1; -/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file2,v +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v done Checking in file2; -/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file3,v +RCS file: ${TESTDIR}/cvsroot/first-dir/file3,v done Checking in file3; -/tmp/cvs-sanity/cvsroot/first-dir/file3,v <-- file3 +${TESTDIR}/cvsroot/first-dir/file3,v <-- file3 initial revision: 1.1 done -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v +RCS file: ${TESTDIR}/cvsroot/first-dir/file4,v done Checking in file4; -/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 initial revision: 1.1 -done' +done +HERE echo 4:trunk-2 >file4 dotest branches-3.2 "${testcvs} -q ci -m trunk-before-branch" \ -'Checking in file4; -/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.2; previous revision: 1.1 -done' +"Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.2; previous revision: 1\.1 +done" dotest branches-4 "${testcvs} tag -b br1" "${PROG}"' [a-z]*: Tagging \. T file1 T file2 @@ -1443,18 +2633,18 @@ T file4' echo 2:br1 >file2 echo 4:br1 >file4 dotest branches-6 "${testcvs} -q ci -m modify" \ -'Checking in file1; -/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 -new revision: 1.1.2.1; previous revision: 1.1 +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 done Checking in file2; -/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 -new revision: 1.1.2.1; previous revision: 1.1 +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.1\.2\.1; previous revision: 1\.1 done Checking in file4; -/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.2.2.1; previous revision: 1.2 -done' +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.2\.2\.1; previous revision: 1\.2 +done" dotest branches-7 "${testcvs} -q tag -b brbr" 'T file1 T file2 T file3 @@ -1463,14 +2653,14 @@ T file4' echo 1:brbr >file1 echo 4:brbr >file4 dotest branches-9 "${testcvs} -q ci -m modify" \ -'Checking in file1; -/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 -new revision: 1.1.2.1.2.1; previous revision: 1.1.2.1 +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.1\.2\.1; previous revision: 1\.1\.2\.1 done Checking in file4; -/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.2.2.1.2.1; previous revision: 1.2.2.1 -done' +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.2\.2\.1\.2\.1; previous revision: 1\.2\.2\.1 +done" dotest branches-10 "cat file1 file2 file3 file4" '1:brbr 2:br1 3:ancest @@ -1484,10 +2674,10 @@ done' 4:br1' echo 4:br1-2 >file4 dotest branches-12.2 "${testcvs} -q ci -m change-on-br1" \ -'Checking in file4; -/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.2.2.2; previous revision: 1.2.2.1 -done' +"Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1 +done" dotest branches-13 "${testcvs} -q update -A" '[UP] file1 [UP] file2 [UP] file4' @@ -1498,13 +2688,13 @@ done' echo 4:trunk-3 >file4 dotest branches-14.2 \ "${testcvs} -q ci -m trunk-change-after-branch" \ -'Checking in file4; -/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.3; previous revision: 1.2 -done' +"Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.3; previous revision: 1\.2 +done" dotest branches-14.3 "${testcvs} log file4" \ -' -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v +" +RCS file: ${TESTDIR}/cvsroot/first-dir/file4,v Working file: file4 head: 1\.3 branch: @@ -1518,36 +2708,36 @@ total revisions: 6; selected revisions: 6 description: ---------------------------- revision 1\.3 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: ${PLUS}1 -1 trunk-change-after-branch ---------------------------- revision 1\.2 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: ${PLUS}1 -1 branches: 1\.2\.2; trunk-before-branch ---------------------------- revision 1\.1 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; add-it ---------------------------- revision 1\.2\.2\.2 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: ${PLUS}1 -1 change-on-br1 ---------------------------- revision 1\.2\.2\.1 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: ${PLUS}1 -1 branches: 1\.2\.2\.1\.2; modify ---------------------------- revision 1\.2\.2\.1\.2\.1 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: ${PLUS}1 -1 modify -=============================================================================' +=============================================================================" dotest_status branches-14.4 1 \ "${testcvs} diff -c -r 1.1 -r 1.3 file4" \ -'Index: file4 +"Index: file4 =================================================================== -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v +RCS file: ${TESTDIR}/cvsroot/first-dir/file4,v retrieving revision 1\.1 retrieving revision 1\.3 diff -c -r1\.1 -r1\.3 @@ -1557,12 +2747,12 @@ diff -c -r1\.1 -r1\.3 \*\*\* 1 \*\*\*\* ! 4:trunk-1 --- 1 ---- -! 4:trunk-3' +! 4:trunk-3" dotest_status branches-14.5 1 \ "${testcvs} diff -c -r 1.1 -r 1.2.2.1 file4" \ -'Index: file4 +"Index: file4 =================================================================== -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v +RCS file: ${TESTDIR}/cvsroot/first-dir/file4,v retrieving revision 1\.1 retrieving revision 1\.2\.2\.1 diff -c -r1\.1 -r1\.2\.2\.1 @@ -1572,23 +2762,106 @@ diff -c -r1\.1 -r1\.2\.2\.1 \*\*\* 1 \*\*\*\* ! 4:trunk-1 --- 1 ---- -! 4:br1' +! 4:br1" dotest branches-15 \ "${testcvs} update -j 1.1.2.1 -j 1.1.2.1.2.1 file1" \ - 'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v -retrieving revision 1.1.2.1 -retrieving revision 1.1.2.1.2.1 -Merging differences between 1.1.2.1 and 1.1.2.1.2.1 into file1 -rcsmerge: warning: conflicts during merge' + "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +retrieving revision 1\.1\.2\.1 +retrieving revision 1\.1\.2\.1\.2\.1 +Merging differences between 1\.1\.2\.1 and 1\.1\.2\.1\.2\.1 into file1 +rcsmerge: warning: conflicts during merge" dotest branches-16 "cat file1" '<<<<<<< file1 1:ancest ======= 1:brbr ->>>>>>> 1.1.2.1.2.1' +[>]>>>>>> 1\.1\.2\.1\.2\.1' + cd .. + + if test "$keep" = yes; then + echo Keeping ${TESTDIR} and exiting due to --keep + exit 0 + fi + + rm -rf ${CVSROOT_DIRNAME}/first-dir + rm -r first-dir + ;; + + multibranch) + # Test the ability to have several branchpoints coming off the + # same revision. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest multibranch-1 "${testcvs} -q co first-dir" '' + cd first-dir + echo 1:trunk-1 >file1 + dotest multibranch-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest_lit multibranch-3 "${testcvs} -q ci -m add-it" <<HERE +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1.1 +done +HERE + dotest multibranch-4 "${testcvs} tag -b br1" \ +"${PROG} [a-z]*: Tagging \. +T file1" + dotest multibranch-5 "${testcvs} tag -b br2" \ +"${PROG} [a-z]*: Tagging \. +T file1" + dotest multibranch-6 "${testcvs} -q update -r br1" '' + echo on-br1 >file1 + dotest multibranch-7 "${testcvs} -q ci -m modify-on-br1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + dotest multibranch-8 "${testcvs} -q update -r br2" '[UP] file1' + echo br2 adds a line >>file1 + dotest multibranch-9 "${testcvs} -q ci -m modify-on-br2" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.4\.1; previous revision: 1\.1 +done" + dotest multibranch-10 "${testcvs} -q update -r br1" '[UP] file1' + dotest multibranch-11 "cat file1" 'on-br1' + dotest multibranch-12 "${testcvs} -q update -r br2" '[UP] file1' + dotest multibranch-13 "cat file1" '1:trunk-1 +br2 adds a line' + + dotest multibranch-14 "${testcvs} log file1" \ +" +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +symbolic names: + br2: 1\.1\.0\.4 + br1: 1\.1\.0\.2 +keyword substitution: kv +total revisions: 3; selected revisions: 3 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +branches: 1\.1\.2; 1\.1\.4; +add-it +---------------------------- +revision 1\.1\.4\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +modify-on-br2 +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -1 +modify-on-br1 +=============================================================================" cd .. if test "$keep" = yes; then - echo Keeping /tmp/cvs-sanity and exiting due to --keep + echo Keeping ${TESTDIR} and exiting due to --keep exit 0 fi @@ -1601,7 +2874,7 @@ rcsmerge: warning: conflicts during merge' mkdir import-dir ; cd import-dir for i in 1 2 3 4 ; do - echo imported file"$i" > imported-file"$i" + echo imported file"$i" > imported-f"$i" done # This directory should be on the default ignore list, @@ -1609,8 +2882,8 @@ rcsmerge: warning: conflicts during merge' mkdir RCS echo ignore.me >RCS/ignore.me - echo 'import should not expand $''Id$' >>imported-file2 - cp imported-file2 ../imported-file2-orig.tmp + echo 'import should not expand $''Id$' >>imported-f2 + cp imported-f2 ../imported-f2-orig.tmp if ${CVS} import -m first-import first-dir vendor-branch junk-1_0 ; then echo "PASS: test 96" >>${LOGFILE} @@ -1618,7 +2891,7 @@ rcsmerge: warning: conflicts during merge' echo "FAIL: test 96" | tee -a ${LOGFILE} ; exit 1 fi - if cmp ../imported-file2-orig.tmp imported-file2; then + if cmp ../imported-f2-orig.tmp imported-f2; then pass 96.5 else fail 96.5 @@ -1634,7 +2907,7 @@ rcsmerge: warning: conflicts during merge' cd first-dir for i in 1 2 3 4 ; do - if [ -f imported-file"$i" ] ; then + if test -f imported-f"$i" ; then echo "PASS: test 98-$i" >>${LOGFILE} else echo "FAIL: test 98-$i" | tee -a ${LOGFILE} ; exit 1 @@ -1647,18 +2920,15 @@ rcsmerge: warning: conflicts during merge' fi # remove - rm imported-file1 - if ${CVS} rm imported-file1 2>> ${LOGFILE}; then + rm imported-f1 + if ${CVS} rm imported-f1 2>> ${LOGFILE}; then echo "PASS: test 99" >>${LOGFILE} else echo "FAIL: test 99" | tee -a ${LOGFILE} ; exit 1 fi # change - # this sleep is significant. Otherwise, on some machines, things happen so - # fast that the file mod times do not differ. - sleep 1 - echo local-change >> imported-file2 + echo local-change >> imported-f2 # commit if ${CVS} ci -m local-changes >> ${LOGFILE} 2>&1; then @@ -1668,7 +2938,7 @@ rcsmerge: warning: conflicts during merge' fi # log - if ${CVS} log imported-file1 | grep '1.1.1.2 (dead)' ; then + if ${CVS} log imported-f1 | grep '1.1.1.2 (dead)' ; then echo "FAIL: test 101" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 101" >>${LOGFILE} @@ -1682,16 +2952,16 @@ rcsmerge: warning: conflicts during merge' fi # remove file4 on the vendor branch - rm imported-file4 + rm imported-f4 - if ${CVS} rm imported-file4 2>> ${LOGFILE}; then + if ${CVS} rm imported-f4 2>> ${LOGFILE}; then echo "PASS: test 103" >>${LOGFILE} else echo "FAIL: test 103" | tee -a ${LOGFILE} ; exit 1 fi # commit - if ${CVS} ci -m vendor-removed imported-file4 >>${LOGFILE}; then + if ${CVS} ci -m vendor-removed imported-f4 >>${LOGFILE}; then echo "PASS: test 104" >>${LOGFILE} else echo "FAIL: test 104" | tee -a ${LOGFILE} ; exit 1 @@ -1707,16 +2977,16 @@ rcsmerge: warning: conflicts during merge' # second import - file4 deliberately unchanged cd ../import-dir for i in 1 2 3 ; do - echo rev 2 of file $i >> imported-file"$i" + echo rev 2 of file $i >> imported-f"$i" done - cp imported-file2 ../imported-file2-orig.tmp + cp imported-f2 ../imported-f2-orig.tmp if ${CVS} import -m second-import first-dir vendor-branch junk-2_0 ; then echo "PASS: test 106" >>${LOGFILE} else echo "FAIL: test 106" | tee -a ${LOGFILE} ; exit 1 fi - if cmp ../imported-file2-orig.tmp imported-file2; then + if cmp ../imported-f2-orig.tmp imported-f2; then pass 106.5 else fail 106.5 @@ -1732,14 +3002,14 @@ rcsmerge: warning: conflicts during merge' cd first-dir - if [ -f imported-file1 ] ; then + if test -f imported-f1 ; then echo "FAIL: test 108" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 108" >>${LOGFILE} fi for i in 2 3 ; do - if [ -f imported-file"$i" ] ; then + if test -f imported-f"$i" ; then echo "PASS: test 109-$i" >>${LOGFILE} else echo "FAIL: test 109-$i" | tee -a ${LOGFILE} ; exit 1 @@ -1753,7 +3023,7 @@ rcsmerge: warning: conflicts during merge' echo "FAIL: test 110" | tee -a ${LOGFILE} ; exit 1 fi - if [ -f imported-file4 ] ; then + if test -f imported-f4 ; then echo "PASS: test 111" >>${LOGFILE} else echo "FAIL: test 111" | tee -a ${LOGFILE} ; exit 1 @@ -1768,37 +3038,386 @@ rcsmerge: warning: conflicts during merge' cd .. - if ${CVS} co -jjunk-1_0 -jjunk-2_0 first-dir >>${LOGFILE} 2>&1; then - echo "PASS: test 113" >>${LOGFILE} - else - echo "FAIL: test 113" | tee -a ${LOGFILE} ; exit 1 - fi + dotest import-113 \ +"${testcvs} -q co -jjunk-1_0 -jjunk-2_0 first-dir" \ +"${PROG} [a-z]*: file first-dir/imported-f1 is present in revision junk-2_0 +RCS file: ${TESTDIR}/cvsroot/first-dir/imported-f2,v +retrieving revision 1\.1\.1\.1 +retrieving revision 1\.1\.1\.2 +Merging differences between 1\.1\.1\.1 and 1\.1\.1\.2 into imported-f2 +rcsmerge: warning: conflicts during merge" cd first-dir - if [ -f imported-file1 ] ; then + if test -f imported-f1 ; then echo "FAIL: test 114" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 114" >>${LOGFILE} fi for i in 2 3 ; do - if [ -f imported-file"$i" ] ; then + if test -f imported-f"$i" ; then echo "PASS: test 115-$i" >>${LOGFILE} else echo "FAIL: test 115-$i" | tee -a ${LOGFILE} ; exit 1 fi done - if cat imported-file2 | grep '====' >> ${LOGFILE}; then - echo "PASS: test 116" >>${LOGFILE} - else - echo "FAIL: test 116" | tee -a ${LOGFILE} ; exit 1 - fi + dotest import-116 'cat imported-f2' \ +'imported file2 +[<]<<<<<< imported-f2 +import should not expand \$''Id: imported-f2,v 1\.2 [0-9/]* [0-9:]* [a-z0-9@][a-z0-9@]* Exp \$ +local-change +[=]====== +import should not expand \$''Id: imported-f2,v 1\.1\.1\.2 [0-9/]* [0-9:]* [a-z0-9@][a-z0-9@]* Exp \$ +rev 2 of file 2 +[>]>>>>>> 1\.1\.1\.2' + cd .. ; rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir rm -rf import-dir ;; + join) + # Test doing joins which involve adding and removing files. + # See also binfile2, which does similar things with binary files. + + # We check merging changes from T1 to T2 into the main line. + # Here are the interesting cases I can think of: + # 1) File added between T1 and T2, not on main line. + # File should be marked for addition. + # 2) File added between T1 and T2, also added on main line. + # Conflict. + # 3) File removed between T1 and T2, unchanged on main line. + # File should be marked for removal. + # 4) File removed between T1 and T2, modified on main line. + # If mod checked in, file should be marked for removal. + # If mod still in working directory, conflict. + # 5) File removed between T1 and T2, was never on main line. + # Nothing should happen. + # 6) File removed between T1 and T2, also removed on main line. + # Nothing should happen. + # 7) File added on main line, not added between T1 and T2. + # Nothing should happen. + # 8) File removed on main line, not modified between T1 and T2. + # Nothing should happen. + + # We also check merging changes from a branch into the main + # line. Here are the interesting cases: + # 1) File added on branch, not on main line. + # File should be marked for addition. + # 2) File added on branch, also added on main line. + # Conflict. + # 3) File removed on branch, unchanged on main line. + # File should be marked for removal. + # 4) File removed on branch, modified on main line. + # Conflict. + # 5) File removed on branch, was never on main line. + # Nothing should happen. + # 6) File removed on branch, also removed on main line. + # Nothing should happen. + # 7) File added on main line, not added on branch. + # Nothing should happen. + # 8) File removed on main line, not modified on branch. + # Nothing should happen. + + # In the tests below, fileN represents case N in the above + # lists. + + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest join-1 "${testcvs} -q co first-dir" '' + + cd first-dir + + # Add two files. + echo 'first revision of file3' > file3 + echo 'first revision of file4' > file4 + echo 'first revision of file6' > file6 + echo 'first revision of file8' > file8 + dotest join-2 "${testcvs} add file3 file4 file6 file8" \ +"${PROG}"' [a-z]*: scheduling file `file3'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file4'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file6'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file8'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' + + dotest join-3 "${testcvs} -q commit -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file3,v +done +Checking in file3; +${TESTDIR}/cvsroot/first-dir/file3,v <-- file3 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file4,v +done +Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file6,v +done +Checking in file6; +${TESTDIR}/cvsroot/first-dir/file6,v <-- file6 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file8,v +done +Checking in file8; +${TESTDIR}/cvsroot/first-dir/file8,v <-- file8 +initial revision: 1\.1 +done" + + # Make a branch. + dotest join-4 "${testcvs} -q tag -b branch ." \ +'T file3 +T file4 +T file6 +T file8' + + # Add file2 and file7, modify file4, and remove file6 and file8. + echo 'first revision of file2' > file2 + echo 'second revision of file4' > file4 + echo 'first revision of file7' > file7 + rm file6 file8 + dotest join-5 "${testcvs} add file2 file7" \ +"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file7'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' + dotest join-6 "${testcvs} rm file6 file8" \ +"${PROG}"' [a-z]*: scheduling `file6'\'' for removal +'"${PROG}"' [a-z]*: scheduling `file8'\'' for removal +'"${PROG}"' [a-z]*: use '\'"${PROG} commit"\'' to remove these files permanently' + dotest join-7 "${testcvs} -q ci -mx ." \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done +Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.2; previous revision: 1\.1 +done +Removing file6; +${TESTDIR}/cvsroot/first-dir/file6,v <-- file6 +new revision: delete; previous revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file7,v +done +Checking in file7; +${TESTDIR}/cvsroot/first-dir/file7,v <-- file7 +initial revision: 1\.1 +done +Removing file8; +${TESTDIR}/cvsroot/first-dir/file8,v <-- file8 +new revision: delete; previous revision: 1\.1 +done" + + # Check out the branch. + cd ../.. + mkdir 2 + cd 2 + dotest join-8 "${testcvs} -q co -r branch first-dir" \ +'U first-dir/file3 +U first-dir/file4 +U first-dir/file6 +U first-dir/file8' + + cd first-dir + + # Modify the files on the branch, so that T1 is not an + # ancestor of the main line, and add file5 + echo 'first branch revision of file3' > file3 + echo 'first branch revision of file4' > file4 + echo 'first branch revision of file6' > file6 + echo 'first branch revision of file5' > file5 + dotest join-9 "${testcvs} add file5" \ +"${PROG}"' [a-z]*: scheduling file `file5'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest join-10 "${testcvs} -q ci -mx ." \ +"Checking in file3; +${TESTDIR}/cvsroot/first-dir/file3,v <-- file3 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/file5,v +done +Checking in file5; +${TESTDIR}/cvsroot/first-dir/Attic/file5,v <-- file5 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Checking in file6; +${TESTDIR}/cvsroot/first-dir/Attic/file6,v <-- file6 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + + # Tag the current revisions on the branch. + dotest join-11 "${testcvs} -q tag T1 ." \ +'T file3 +T file4 +T file5 +T file6 +T file8' + + # Add file1 and file2, and remove the other files. + echo 'first branch revision of file1' > file1 + echo 'first branch revision of file2' > file2 + rm file3 file4 file5 file6 + dotest join-12 "${testcvs} add file1 file2" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' + dotest join-13 "${testcvs} rm file3 file4 file5 file6" \ +"${PROG}"' [a-z]*: scheduling `file3'\'' for removal +'"${PROG}"' [a-z]*: scheduling `file4'\'' for removal +'"${PROG}"' [a-z]*: scheduling `file5'\'' for removal +'"${PROG}"' [a-z]*: scheduling `file6'\'' for removal +'"${PROG}"' [a-z]*: use '\'"${PROG} commit"\'' to remove these files permanently' + dotest join-14 "${testcvs} -q ci -mx ." \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/Attic/file1,v <-- file1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Removing file3; +${TESTDIR}/cvsroot/first-dir/file3,v <-- file3 +new revision: delete; previous revision: 1\.1\.2\.1 +done +Removing file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: delete; previous revision: 1\.1\.2\.1 +done +Removing file5; +${TESTDIR}/cvsroot/first-dir/Attic/file5,v <-- file5 +new revision: delete; previous revision: 1\.1\.2\.1 +done +Removing file6; +${TESTDIR}/cvsroot/first-dir/Attic/file6,v <-- file6 +new revision: delete; previous revision: 1\.1\.2\.1 +done" + + # Tag the current revisions on the branch. + dotest join-15 "${testcvs} -q tag T2 ." \ +'T file1 +T file2 +T file8' + + # Do a checkout with a merge. + cd ../.. + mkdir 3 + cd 3 + dotest join-16 "${testcvs} -q co -jT1 -jT2 first-dir" \ +'U first-dir/file1 +U first-dir/file2 +'"${PROG}"' [a-z]*: file first-dir/file2 exists, but has been added in revision T2 +U first-dir/file3 +'"${PROG}"' [a-z]*: scheduling first-dir/file3 for removal +U first-dir/file4 +'"${PROG}"' [a-z]*: scheduling first-dir/file4 for removal +U first-dir/file7' + + # Verify that the right changes have been scheduled. + cd first-dir + dotest join-17 "${testcvs} -q update" \ +'A file1 +R file3 +R file4' + + # Modify file4 locally, and do an update with a merge. + cd ../../1/first-dir + echo 'third revision of file4' > file4 + dotest join-18 "${testcvs} -q update -jT1 -jT2 ." \ +'U file1 +'"${PROG}"' [a-z]*: file file2 exists, but has been added in revision T2 +'"${PROG}"' [a-z]*: scheduling file3 for removal +M file4 +'"${PROG}"' [a-z]*: file file4 is locally modified, but has been removed in revision T2' + + # Verify that the right changes have been scheduled. + dotest join-19 "${testcvs} -q update" \ +'A file1 +R file3 +M file4' + + # Do a checkout with a merge from a single revision. + + # FIXME: CVS currently gets this wrong. file2 has been + # added on both the branch and the main line, and so should + # be regarded as a conflict. However, given the way that + # CVS sets up the RCS file, there is no way to distinguish + # this case from the case of file2 having existed before the + # branch was made. This could be fixed by reserving + # a revision somewhere, perhaps 1.1, as an always dead + # revision which can be used as the source for files added + # on branches. + cd ../../3 + rm -rf first-dir + dotest join-20 "${testcvs} -q co -jbranch first-dir" \ +"U first-dir/file1 +U first-dir/file2 +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +retrieving revision 1\.1 +retrieving revision 1\.1\.2\.1 +Merging differences between 1\.1 and 1\.1\.2\.1 into file2 +U first-dir/file3 +${PROG} [a-z]*: scheduling first-dir/file3 for removal +U first-dir/file4 +${PROG} [a-z]*: file first-dir/file4 has been modified, but has been removed in revision branch +U first-dir/file7" + + # Verify that the right changes have been scheduled. + # The M file2 line is a bug; see above join-20. + cd first-dir + dotest join-21 "${testcvs} -q update" \ +'A file1 +M file2 +R file3' + + # Checkout the main line again. + cd ../../1 + rm -rf first-dir + dotest join-22 "${testcvs} -q co first-dir" \ +'U first-dir/file2 +U first-dir/file3 +U first-dir/file4 +U first-dir/file7' + + # Modify file4 locally, and do an update with a merge from a + # single revision. + # The file2 handling is a bug; see above join-20. + cd first-dir + echo 'third revision of file4' > file4 + dotest join-23 "${testcvs} -q update -jbranch ." \ +"U file1 +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +retrieving revision 1\.1 +retrieving revision 1\.1\.2\.1 +Merging differences between 1\.1 and 1\.1\.2\.1 into file2 +${PROG} [a-z]*: scheduling file3 for removal +M file4 +${PROG} [a-z]*: file file4 is locally modified, but has been removed in revision branch" + + # Verify that the right changes have been scheduled. + # The M file2 line is a bug; see above join-20 + dotest join-24 "${testcvs} -q update" \ +'A file1 +M file2 +R file3 +M file4' + + cd ../.. + rm -rf 1 2 3 ${CVSROOT_DIRNAME}/first-dir + ;; + new) # look for stray "no longer pertinent" messages. mkdir ${CVSROOT_DIRNAME}/first-dir @@ -1852,38 +3471,128 @@ rcsmerge: warning: conflicts during merge' cd .. ; rm -rf first-dir ; rm -rf ${CVSROOT_DIRNAME}/first-dir ;; + newb) + # Test removing a file on a branch and then checking it out. + + # We call this "newb" only because it, like the "new" tests, + # has something to do with "no longer pertinent" messages. + # Not necessarily the most brilliant nomenclature. + + # Create file 'a'. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest newb-123a "${testcvs} -q co first-dir" '' + cd first-dir + touch a + dotest newb-123b "${testcvs} add a" \ +"${PROG} [a-z]*: scheduling file .a. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest newb-123c "${testcvs} -q ci -m added" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/a,v +done +Checking in a; +${TESTDIR}/cvsroot/first-dir/a,v <-- a +initial revision: 1\.1 +done" + + # Make a branch. + dotest newb-123d "${testcvs} -q tag -b branch" "T a" + + # Check out the branch. + cd .. + rm -rf first-dir + mkdir 1 + cd 1 + dotest newb-123e "${testcvs} -q co -r branch first-dir" \ +"U first-dir/a" + + # Remove 'a' on another copy of the branch. + cd .. + mkdir 2 + cd 2 + dotest newb-123f "${testcvs} -q co -r branch first-dir" \ +"U first-dir/a" + cd first-dir + rm a + dotest newb-123g "${testcvs} rm a" \ +"${PROG} [a-z]*: scheduling .a. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + dotest newb-123h "${testcvs} -q ci -m removed" \ +"Removing a; +${TESTDIR}/cvsroot/first-dir/a,v <-- a +new revision: delete; previous revision: 1\.1\.2 +done" + + # Check out the file on the branch. This should report + # that the file is not pertinent, but it should not + # say anything else. + cd .. + rm -rf first-dir + dotest newb-123i "${testcvs} -q co -r branch first-dir/a" \ +"${PROG} [a-z]*: warning: first-dir/a is not (any longer) pertinent" + + # Update the other copy, and make sure that a is removed. + cd ../1/first-dir + # "Needs Patch" is a rather strange output here. Something like + # "Removed in Repository" would make more sense. + # The "Need Checkout" output is what CVS does if configured + # --disable-server. + dotest newb-123j0 "${testcvs} status a" \ +"=================================================================== +File: a Status: Needs \(Patch\|Checkout\) + + Working revision: 1\.1.* + Repository revision: 1\.1\.2\.1 ${TESTDIR}/cvsroot/first-dir/a,v + Sticky Tag: branch (branch: 1\.1\.2) + Sticky Date: (none) + Sticky Options: (none)" + dotest newb-123j "${testcvs} -q update" \ +"${PROG} [a-z]*: warning: a is not (any longer) pertinent" + + if test -f a; then + fail newb-123k + else + pass newb-123k + fi + + cd ../.. + rm -rf 1 2 ; rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + conflicts) - rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir mkdir ${CVSROOT_DIRNAME}/first-dir mkdir 1 cd 1 - if ${CVS} co first-dir ; then - echo 'PASS: test 124' >>${LOGFILE} - else - echo 'FAIL: test 124' | tee -a ${LOGFILE} - fi + dotest conflicts-124 "${testcvs} -q co first-dir" '' cd first-dir touch a - if ${CVS} add a 2>>${LOGFILE} ; then - echo 'PASS: test 125' >>${LOGFILE} - else - echo 'FAIL: test 125' | tee -a ${LOGFILE} - fi - - if ${CVS} ci -m added >>${LOGFILE} 2>&1; then - echo 'PASS: test 126' >>${LOGFILE} - else - echo 'FAIL: test 126' | tee -a ${LOGFILE} - fi + dotest conflicts-125 "${testcvs} add a" \ +"${PROG} [a-z]*: scheduling file .a. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest conflicts-126 "${testcvs} -q ci -m added" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/a,v +done +Checking in a; +${TESTDIR}/cvsroot/first-dir/a,v <-- a +initial revision: 1\.1 +done" cd ../.. mkdir 2 cd 2 + # The need for TMPPWD here is a (minor) CVS bug; the + # output should use the name of the repository as specified. + dotest conflicts-126.5 "${testcvs} co -p first-dir" \ +"${PROG} [a-z]*: Updating first-dir +=================================================================== +Checking out first-dir/a +RCS: ${TMPPWD}/cvsroot/first-dir/a,v +VERS: 1\.1 +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*" if ${CVS} co first-dir ; then echo 'PASS: test 127' >>${LOGFILE} else @@ -1900,62 +3609,122 @@ rcsmerge: warning: conflicts during merge' echo add a line >>a mkdir dir1 dotest conflicts-127b "${testcvs} add dir1" \ -'Directory /tmp/cvs-sanity/cvsroot/first-dir/dir1 added to the repository' +"Directory ${TESTDIR}/cvsroot/first-dir/dir1 added to the repository" dotest conflicts-128 "${testcvs} -q ci -m changed" \ -'Checking in a; -/tmp/cvs-sanity/cvsroot/first-dir/a,v <-- a -new revision: 1.2; previous revision: 1.1 -done' - cd ../../2/first-dir +"Checking in a; +${TESTDIR}/cvsroot/first-dir/a,v <-- a +new revision: 1\.2; previous revision: 1\.1 +done" + cd ../.. + + # Similar to conflicts-126.5, but now the file has nonempty + # contents. + mkdir 3 + cd 3 + # The need for TMPPWD here is a (minor) CVS bug; the + # output should use the name of the repository as specified. + dotest conflicts-128.5 "${testcvs} co -p -l first-dir" \ +"${PROG} [a-z]*: Updating first-dir +=================================================================== +Checking out first-dir/a +RCS: ${TMPPWD}/cvsroot/first-dir/a,v +VERS: 1\.2 +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +add a line" + cd .. + rmdir 3 + + # Now go over the to the other working directory and + # start testing conflicts + cd 2/first-dir echo add a conflicting line >>a dotest_fail conflicts-129 "${testcvs} -q ci -m changed" \ "${PROG}"' [a-z]*: Up-to-date check failed for `a'\'' '"${PROG}"' \[[a-z]* aborted\]: correct above errors first!' mkdir dir1 mkdir sdir + dotest conflicts-status-0 "${testcvs} status a" \ +"=================================================================== +File: a Status: Needs Merge + + Working revision: 1\.1.* + Repository revision: 1\.2 ${TESTDIR}/cvsroot/first-dir/a,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" dotest conflicts-130 "${testcvs} -q update" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/a,v -retrieving revision 1.1 -retrieving revision 1.2 -Merging differences between 1.1 and 1.2 into a +"RCS file: ${TESTDIR}/cvsroot/first-dir/a,v +retrieving revision 1\.1 +retrieving revision 1\.2 +Merging differences between 1\.1 and 1\.2 into a rcsmerge: warning: conflicts during merge -'"${PROG}"' [a-z]*: conflicts found in a +${PROG} [a-z]*: conflicts found in a C a -'"${QUESTION}"' dir1 -'"${QUESTION}"' sdir' \ -''"${QUESTION}"' dir1 -'"${QUESTION}"' sdir -RCS file: /tmp/cvs-sanity/cvsroot/first-dir/a,v -retrieving revision 1.1 -retrieving revision 1.2 -Merging differences between 1.1 and 1.2 into a +${QUESTION} dir1 +${QUESTION} sdir" \ +"${QUESTION} dir1 +${QUESTION} sdir +RCS file: ${TESTDIR}/cvsroot/first-dir/a,v +retrieving revision 1\.1 +retrieving revision 1\.2 +Merging differences between 1\.1 and 1\.2 into a rcsmerge: warning: conflicts during merge -'"${PROG}"' [a-z]*: conflicts found in a -C a' +${PROG} [a-z]*: conflicts found in a +C a" + rmdir dir1 sdir - # Try to check in the file with the conflict markers in it. - if ${CVS} ci -m try 2>>${LOGFILE}; then - echo 'FAIL: test 131' | tee -a ${LOGFILE} - else - # Should tell us to resolve conflict first - echo 'PASS: test 131' >>${LOGFILE} - fi + dotest conflicts-status-1 "${testcvs} status a" \ +"=================================================================== +File: a Status: File had conflicts on merge + + Working revision: 1\.2.* + Repository revision: 1\.2 ${TESTDIR}/cvsroot/first-dir/a,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + dotest_fail conflicts-131 "${testcvs} -q ci -m try" \ +"${PROG} [a-z]*: file .a. had a conflict and has not been modified +${PROG} \[[a-z]* aborted\]: correct above errors first!" echo lame attempt at resolving it >>a # Try to check in the file with the conflict markers in it. - if ${CVS} ci -m try >>${LOGFILE} 2>&1; then - echo 'FAIL: test 132' | tee -a ${LOGFILE} - else - # Should tell us to resolve conflict first - echo 'PASS: test 132' >>${LOGFILE} - fi + dotest conflicts-status-2 "${testcvs} status a" \ +"=================================================================== +File: a Status: File had conflicts on merge + + Working revision: 1\.2.* + Repository revision: 1\.2 ${TESTDIR}/cvsroot/first-dir/a,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + dotest_fail conflicts-132 "${testcvs} -q ci -m try" \ +"${PROG} [a-z]*: file .a. still contains conflict indicators +${PROG} \[[a-z]* aborted\]: correct above errors first!" echo resolve conflict >a - if ${CVS} ci -m resolved >>${LOGFILE} 2>&1; then - echo 'PASS: test 133' >>${LOGFILE} - else - echo 'FAIL: test 133' | tee -a ${LOGFILE} - fi + dotest conflicts-status-3 "${testcvs} status a" \ +"=================================================================== +File: a Status: File had conflicts on merge + + Working revision: 1\.2.* + Repository revision: 1\.2 ${TESTDIR}/cvsroot/first-dir/a,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + dotest conflicts-133 "${testcvs} -q ci -m resolved" \ +"Checking in a; +${TESTDIR}/cvsroot/first-dir/a,v <-- a +new revision: 1\.3; previous revision: 1\.2 +done" + dotest conflicts-status-4 "${testcvs} status a" \ +"=================================================================== +File: a Status: Up-to-date + + Working revision: 1\.3.* + Repository revision: 1\.3 ${TESTDIR}/cvsroot/first-dir/a,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" # Now test that we can add a file in one working directory # and have an update in another get it. @@ -1972,7 +3741,8 @@ C a' echo 'FAIL: test 135' | tee -a ${LOGFILE} fi cd ../../2 - dotest conflicts-136 "${testcvs} -q update" \ + mkdir first-dir/dir1 first-dir/sdir + dotest conflicts-136 "${testcvs} -q update first-dir" \ '[UP] first-dir/abc '"${QUESTION}"' first-dir/dir1 '"${QUESTION}"' first-dir/sdir' \ @@ -2024,12 +3794,132 @@ C a' else echo 'FAIL: test 142' | tee -a ${LOGFILE} fi - - cd ../.. + cd ../.. rm -rf 1 2 3 ; rm -rf ${CVSROOT_DIRNAME}/first-dir ;; + + conflicts2) + # More conflicts tests; separate from conflicts to keep each + # test a manageable size. + mkdir ${CVSROOT_DIRNAME}/first-dir + + mkdir 1 + cd 1 + + dotest conflicts2-142a1 "${testcvs} -q co first-dir" '' + + cd first-dir + touch a abc + + dotest conflicts2-142a2 "${testcvs} add a abc" \ +"${PROG} [a-z]*: scheduling file .a. for addition +${PROG} [a-z]*: scheduling file .abc. for addition +${PROG} [a-z]*: use .cvs commit. to add these files permanently" + dotest conflicts2-142a3 "${testcvs} -q ci -m added" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/a,v +done +Checking in a; +${TESTDIR}/cvsroot/first-dir/a,v <-- a +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/abc,v +done +Checking in abc; +${TESTDIR}/cvsroot/first-dir/abc,v <-- abc +initial revision: 1\.1 +done" + + cd ../.. + mkdir 2 + cd 2 + + dotest conflicts2-142a4 "${testcvs} -q co first-dir" 'U first-dir/a +U first-dir/abc' + cd .. + + # Now test that if one person modifies and commits a + # file and a second person removes it, it is a + # conflict + cd 1/first-dir + echo modify a >>a + dotest conflicts2-142b2 "${testcvs} -q ci -m modify-a" \ +"Checking in a; +${TESTDIR}/cvsroot/first-dir/a,v <-- a +new revision: 1\.2; previous revision: 1\.1 +done" + cd ../../2/first-dir + rm a + dotest conflicts2-142b3 "${testcvs} rm a" \ +"${PROG} [a-z]*: scheduling .a. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + dotest_fail conflicts2-142b4 "${testcvs} -q update" \ +"${PROG} [a-z]*: conflict: removed a was modified by second party +C a" + # Resolve the conflict by deciding not to remove the file + # after all. + dotest conflicts2-142b5 "${testcvs} add a" "U a +${PROG} [a-z]*: a, version 1\.1, resurrected" + dotest conflicts2-142b6 "${testcvs} -q update" '' + cd ../.. + + # Now test that if one person removes a file and + # commits it, and a second person removes it, is it + # not a conflict. + cd 1/first-dir + rm abc + dotest conflicts2-142c0 "${testcvs} rm abc" \ +"${PROG} [a-z]*: scheduling .abc. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + dotest conflicts2-142c1 "${testcvs} -q ci -m remove-abc" \ +"Removing abc; +${TESTDIR}/cvsroot/first-dir/abc,v <-- abc +new revision: delete; previous revision: 1\.1 +done" + cd ../../2/first-dir + rm abc + dotest conflicts2-142c2 "${testcvs} rm abc" \ +"${PROG} [a-z]*: scheduling .abc. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + dotest conflicts2-142c3 "${testcvs} update" \ +"${PROG} [a-z]*: Updating \." + cd ../.. + + # conflicts2-142d*: test that if one party adds a file, and another + # party has a file of the same name, cvs notices + cd 1/first-dir + touch aa.c + dotest conflicts2-142d0 "${testcvs} add aa.c" \ +"${PROG} [a-z]*: scheduling file .aa\.c. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest conflicts2-142d1 "${testcvs} -q ci -m added" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aa.c,v +done +Checking in aa.c; +${TESTDIR}/cvsroot/first-dir/aa.c,v <-- aa.c +initial revision: 1\.1 +done" + cd ../../2/first-dir + echo "don't you dare obliterate this text" >aa.c + # Doing this test separately for remote and local is a fair + # bit of a kludge, but the exit status differs. I'm not sure + # which exit status is the more appropriate one. + if test "$remote" = yes; then + dotest conflicts2-142d2 "${testcvs} -q update" \ +"${QUESTION} aa\.c +U aa\.c +${PROG} update: move away \./aa\.c; it is in the way" + else + dotest_fail conflicts2-142d2 "${testcvs} -q update" \ +"${PROG} [a-z]*: move away aa\.c; it is in the way +C aa\.c" + fi + cd ../.. + + rm -rf 1 2 ; rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + modules) - rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir + # Tests of various ways to define and use modules. mkdir ${CVSROOT_DIRNAME}/first-dir mkdir 1 @@ -2085,11 +3975,13 @@ C a' exit 1 fi - echo realmodule first-dir/subdir a >>CVSROOT/modules + echo realmodule first-dir/subdir a >CVSROOT/modules echo dirmodule first-dir/subdir >>CVSROOT/modules echo namedmodule -d nameddir first-dir/subdir >>CVSROOT/modules echo aliasmodule -a first-dir/subdir/a >>CVSROOT/modules echo aliasnested -a first-dir/subdir/ssdir >>CVSROOT/modules + echo topfiles -a first-dir/file1 first-dir/file2 >>CVSROOT/modules + echo world -a . >>CVSROOT/modules # Options must come before arguments. It is possible this should # be relaxed at some point (though the result would be bizarre for @@ -2103,18 +3995,19 @@ C a' exit 1 fi cd .. - dotest 148a0 "${testcvs} co -c" 'CVSROOT CVSROOT -aliasmodule -a first-dir/subdir/a + dotest 148a0 "${testcvs} co -c" 'aliasmodule -a first-dir/subdir/a aliasnested -a first-dir/subdir/ssdir bogusalias first-dir/subdir/a -a dirmodule first-dir/subdir namedmodule -d nameddir first-dir/subdir -realmodule first-dir/subdir a' +realmodule first-dir/subdir a +topfiles -a first-dir/file1 first-dir/file2 +world -a .' # I don't know why aliasmodule isn't printed (I would have thought # that it gets printed without the -a; although I'm not sure that # printing expansions without options is useful). - dotest 148a1 "${testcvs} co -s" 'CVSROOT NONE CVSROOT -bogusalias NONE first-dir/subdir/a -a + dotest 148a1 "${testcvs} co -s" \ +'bogusalias NONE first-dir/subdir/a -a dirmodule NONE first-dir/subdir namedmodule NONE first-dir/subdir realmodule NONE first-dir/subdir a' @@ -2258,12 +4151,313 @@ U nameddir/b' dotest modules-155a2 "test -d first-dir/subdir" '' dotest modules-155a3 "test -d first-dir/subdir/ssdir" '' # Test that nothing extraneous got created. - dotest modules-155a4 "ls -1" "first-dir" + dotest modules-155a4 "ls" "first-dir" \ +"CVS +first-dir" cd .. rm -rf 2 + # Test checking out everything. + mkdir 1 + cd 1 + dotest modules-155b "${testcvs} -q co world" \ +"U CVSROOT/${DOTSTAR} +U first-dir/subdir/a +U first-dir/subdir/b" + cd .. + rm -rf 1 + + # Test checking out a module which lists at least two + # specific files twice. At one time, this failed over + # remote CVS. + mkdir 1 + cd 1 + dotest modules-155c1 "${testcvs} -q co first-dir" \ +"U first-dir/subdir/a +U first-dir/subdir/b" + + cd first-dir + echo 'first revision' > file1 + echo 'first revision' > file2 + dotest modules-155c2 "${testcvs} add file1 file2" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' + dotest modules-155c3 "${testcvs} -q ci -m add-it" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done" + + cd .. + rm -rf first-dir + dotest modules-155c4 "${testcvs} -q co topfiles" \ +"U first-dir/file1 +U first-dir/file2" + dotest modules-155c5 "${testcvs} -q co topfiles" "" + cd .. + rm -rf 1 + + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + + modules2) + # More tests of modules, in particular the & feature. + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir ${CVSROOT_DIRNAME}/second-dir + + mkdir 1 + cd 1 + + dotest modules2-1 "${testcvs} -q co CVSROOT/modules" \ +'U CVSROOT/modules' + cd CVSROOT + echo 'ampermodule &first-dir &second-dir' > modules + # Depending on whether the user also ran the modules test + # we will be checking in revision 1.2 or 1.3. + dotest modules2-2 "${testcvs} -q ci -m add-modules" \ +"Checking in modules; +${TESTDIR}/cvsroot/CVSROOT/modules,v <-- modules +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* +done +${PROG} [a-z]*: Rebuilding administrative file database" + + cd .. + + dotest modules2-3 "${testcvs} -q co ampermodule" '' + dotest modules2-4 "test -d ampermodule/first-dir" '' + dotest modules2-5 "test -d ampermodule/second-dir" '' + + # Test ability of cvs release to handle multiple arguments + cd ampermodule + if ${testcvs} release -d first-dir second-dir <<EOF >>${LOGFILE} +yes +yes +EOF + then + pass modules2-6 + else + fail modules2-6 + fi + dotest_fail modules2-7 "test -d first-dir" '' + dotest_fail modules2-8 "test -d second-dir" '' + + cd .. + + # Test that CVS gives an error if one combines -a with + # other options. + cd CVSROOT + echo 'aliasopt -a -d onedir first-dir' >modules + dotest modules2-a0 "${testcvs} -q ci -m add-modules" \ +"Checking in modules; +${TESTDIR}/cvsroot/CVSROOT/modules,v <-- modules +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* +done +${PROG} [a-z]*: Rebuilding administrative file database" + cd .. + dotest_fail modules2-a1 "${testcvs} -q co aliasopt" \ +"${PROG} [a-z]*: -a cannot be specified in the modules file along with other options" \ +"${PROG} [a-z]*: -a cannot be specified in the modules file along with other options +${PROG} \[[a-z]* aborted\]: cannot expand modules" + + # Clean up. + rm -rf CVSROOT + cd .. + rm -rf 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + rm -rf ${CVSROOT_DIRNAME}/second-dir + ;; + + modules3) + # More tests of modules, in particular what happens if several + # modules point to the same file. + + # First just set up a directory first-dir and a file file1 in it. + mkdir 1; cd 1 + + dotest modules3-0 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest modules3-1 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + + cd first-dir + echo file1 >file1 + dotest modules3-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest modules3-3 "${testcvs} -q ci -m add-it" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + cd .. + + dotest modules3-4 "${testcvs} -q co CVSROOT/modules" \ +'U CVSROOT/modules' + cd CVSROOT + cat >modules <<EOF +mod1 -a first-dir/file1 +bigmod -a mod1 first-dir/file1 +namednest -d src/sub/dir first-dir +nestdeeper -d src/sub1/sub2/sub3/dir first-dir +nestshallow -d src/dir second-dir/suba/subb +path/in/modules &mod1 +EOF + dotest modules3-5 "${testcvs} -q ci -m add-modules" \ +"Checking in modules; +${TESTDIR}/cvsroot/CVSROOT/modules,v <-- modules +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* +done +${PROG} [a-z]*: Rebuilding administrative file database" + cd .. + + dotest modules3-6 "${testcvs} -q co bigmod" '' + rm -rf first-dir + dotest modules3-7 "${testcvs} -q co bigmod" 'U first-dir/file1' + cd .. + rm -r 1 + + mkdir 1; cd 1 + mkdir suba + mkdir suba/subb + # This fails to work remote (it doesn't notice the directories, + # I suppose because they contain no files). Bummer, especially + # considering this is a documented technique and everything. + dotest modules3-7a \ +"${testcvs} import -m add-dirs second-dir tag1 tag2" \ +"${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/suba +${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/suba/subb + +No conflicts created by this import" " +No conflicts created by this import" + cd ..; rm -r 1 + mkdir 1; cd 1 + dotest modules3-7b "${testcvs} co second-dir" \ +"${PROG} [a-z]*: Updating second-dir +${PROG} [a-z]*: Updating second-dir/suba +${PROG} [a-z]*: Updating second-dir/suba/subb" \ +"${PROG} server: Updating second-dir" + + if test "x$remote" = xyes; then + cd second-dir + mkdir suba + dotest modules3-7-workaround1 "${testcvs} add suba" \ +"Directory ${TESTDIR}/cvsroot/second-dir/suba added to the repository" + cd suba + mkdir subb + dotest modules3-7-workaround2 "${testcvs} add subb" \ +"Directory ${TESTDIR}/cvsroot/second-dir/suba/subb added to the repository" + cd ../.. + fi + + cd second-dir/suba/subb + touch fileb + dotest modules3-7c "${testcvs} add fileb" \ +"${PROG} [a-z]*: scheduling file .fileb. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest modules3-7d "${testcvs} -q ci -m add-it" \ +"RCS file: ${TESTDIR}/cvsroot/second-dir/suba/subb/fileb,v +done +Checking in fileb; +${TESTDIR}/cvsroot/second-dir/suba/subb/fileb,v <-- fileb +initial revision: 1\.1 +done" + cd ../../.. + cd ..; rm -r 1 + + mkdir 1; cd 1 + dotest modules3-7e "${testcvs} -q co nestshallow" \ +"U src/dir/fileb" + + # Using ${TESTDIR}/cvsroot/second-dir/suba instead of + # ${TESTDIR}/cvsroot/second-dir seems wrong, it seems like the + # 30 Dec 1996 change to build_dirs_and_chdir simply failed + # to consider what to put in CVS/Repository. + # Remote does "${TESTDIR}/cvsroot/\." which seems equally wrong, + # if in a different way, but variety is the spice of life, + # eh? + dotest modules3-7f "cat CVS/Repository" \ +"${TESTDIR}/cvsroot/second-dir/suba" "${TESTDIR}/cvsroot/\." + + dotest modules3-7g "cat src/CVS/Repository" \ +"${TESTDIR}/cvsroot/second-dir/suba" + dotest modules3-7h "cat src/dir/CVS/Repository" \ +"${TESTDIR}/cvsroot/second-dir/suba/subb" + cd ..; rm -r 1 + + mkdir 1 + cd 1 + dotest modules3-8 "${testcvs} -q co namednest" \ +'U src/sub/dir/file1' + dotest modules3-9 "test -f src/sub/dir/file1" '' + cd .. + rm -r 1 + + # Try the same thing, but with the directories nested even + # deeper (deeply enough so they are nested more deeply than + # the number of directories from / to ${TESTDIR}). + mkdir 1 + cd 1 + dotest modules3-10 "${testcvs} -q co nestdeeper" \ +'U src/sub1/sub2/sub3/dir/file1' + dotest modules3-11 "test -f src/sub1/sub2/sub3/dir/file1" '' + + # While we are doing things like twisted uses of '/' (e.g. + # modules3-12), try this one. + if test "x$remote" = xyes; then + dotest_fail modules3-11b \ +"${testcvs} -q update ${TESTDIR}/1/src/sub1/sub2/sub3/dir/file1" \ +"${PROG} .server aborted.: absolute pathname .${TESTDIR}/1/src/sub1/sub2/sub3/dir/file1. illegal for server" + fi # end of remote-only tests + + cd .. + rm -r 1 + + # This one is almost too twisted for words. The pathname output + # in the message from "co" doesn't include the "path/in/modules", + # but those directories do get created (with no CVSADM except + # in "modules" which has a CVSNULLREPOS). + # I'm not sure anyone is relying on this nonsense or whether we + # need to keep doing it, but it is what CVS currently does... + # Skip it for remote; the remote code has the good sense to + # not deal with it (on the minus side it gives + # "internal error: repository string too short." instead of a + # real error). + # I kind of suspect that it would be OK to just make it a fatal + # error to have '/' in a module name. + if test "x$remote" = xno; then + mkdir 1; cd 1 + dotest modules3-12 "${testcvs} -q co path/in/modules" \ +"U first-dir/file1" + dotest modules3-13 "test -f path/in/modules/first-dir/file1" '' + cd ..; rm -r 1 + + # Now here is where it gets seriously bogus. + mkdir 1; cd 1 + dotest modules3-14 \ +"${testcvs} -q rtag tag1 path/in/modules" '' + # CVS creates this even though rtag should *never* affect + # the directory current when it is called! + dotest modules3-15 "test -d path/in/modules" '' + # Just for trivia's sake, rdiff is not similarly vulnerable + # because it passes 0 for run_module_prog to do_module. + cd ..; rm -r 1 + fi # end of tests skipped for remote + rm -rf ${CVSROOT_DIRNAME}/first-dir + rm -rf ${CVSROOT_DIRNAME}/second-dir ;; + mflag) for message in '' ' ' ' ' ' test' ; do @@ -2355,7 +4549,7 @@ U nameddir/b' fi chmod a-w 1dir cd ../1/1dir - rm foo; + rm foo; if ${testcvs} rm foo >>${LOGFILE} 2>&1; then echo 'PASS: test 166' >>${LOGFILE} else @@ -2556,6 +4750,11 @@ EOF fi dotest devcom-a0 "${testcvs} watchers" '' + + # FIXME: This probably should be an error message instead + # of silently succeeding and printing nothing. + dotest devcom-a-nonexist "${testcvs} watchers nonexist" '' + dotest devcom-a1 "${testcvs} watch add" '' dotest devcom-a2 "${testcvs} watchers" \ 'abb [a-z0-9]* edit unedit commit @@ -2564,12 +4763,160 @@ abc [a-z0-9]* edit unedit commit' dotest devcom-a4 "${testcvs} watchers abb" \ 'abb [a-z0-9]* edit commit' + # Check tagging and checking out while we have a CVS + # directory in the repository. + dotest devcom-t0 "${testcvs} -q tag tag" \ +'T abb +T abc' + cd ../.. + mkdir 3 + cd 3 + + # Test commented out because the bug it tests for is not fixed + # The error is: + # cvs watchers: cannot open CVS/Entries for reading: No such file or directory + # cvs: ../../work/ccvs/src/fileattr.c:75: fileattr_read: Assertion `fileattr_stored_repos != ((void *)0)' failed. +: dotest devcom-t-nonexist "${testcvs} watchers nonexist" fixme + + dotest devcom-t1 "${testcvs} -q co -rtag first-dir/abb" \ +'U first-dir/abb' + cd .. + rm -rf 3 + + # Now remove all the file attributes + cd 2/first-dir + dotest devcom-b0 "${testcvs} watch off" '' + dotest devcom-b1 "${testcvs} watch remove" '' + # Test that CVS 1.6 and earlier can handle the repository. + dotest_fail devcom-b2 "test -d ${CVSROOT_DIRNAME}/first-dir/CVS" + + # Now test watching just some, not all, files. + dotest devcom-some0 "${testcvs} watch on abc" '' + cd ../.. + mkdir 3 + cd 3 + dotest devcom-some1 "${testcvs} -q co first-dir" 'U first-dir/abb +U first-dir/abc' + dotest devcom-some2 "test -w first-dir/abb" '' + dotest_fail devcom-some3 "test -w first-dir/abc" '' + cd .. + + if test "$keep" = yes; then + echo Keeping ${TESTDIR} and exiting due to --keep + exit 0 + fi + + rm -rf 1 2 3 ${CVSROOT_DIRNAME}/first-dir + ;; + + devcom2) + # More watch tests, most notably setting watches on + # files in various different states. + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest devcom2-1 "${testcvs} -q co first-dir" '' + cd first-dir + + # This should probably be an error; setting a watch on a totally + # unknown file is more likely to be a typo than intentional. + # But that isn't the currently implemented behavior. + dotest devcom2-2 "${testcvs} watch on w1" '' + + touch w1 w2 w3 nw1 + dotest devcom2-3 "${testcvs} add w1 w2 w3 nw1" "${DOTSTAR}" + # Letting the user set the watch here probably can be considered + # a feature--although it leads to a few potentially strange + # consequences like one user can set the watch and another actually + # adds the file. + dotest devcom2-4 "${testcvs} watch on w2" '' + dotest devcom2-5 "${testcvs} -q ci -m add-them" "${DOTSTAR}" + + # Note that this test differs in a subtle way from devcom-some0; + # in devcom-some0 the watch is creating a new fileattr file, and + # here we are modifying an existing one. + dotest devcom2-6 "${testcvs} watch on w3" '' + + # Now test that all the watches got set on the correct files + # FIXME: CVS should have a way to report whether watches are + # set, I think. The "check it out and see if it read-only" is + # sort of OK, but is complicated by CVSREAD and doesn't help + # if the file is added and not yet committed or some such. + # Probably "cvs status" should report "watch: on" if watch is on + # (and nothing if watch is off, so existing behavior is preserved). cd ../.. + mkdir 2 + cd 2 + dotest devcom2-7 "${testcvs} -q co first-dir" 'U first-dir/nw1 +U first-dir/w1 +U first-dir/w2 +U first-dir/w3' + dotest devcom2-8 "test -w first-dir/nw1" '' + dotest_fail devcom2-9 "test -w first-dir/w1" '' + dotest_fail devcom2-10 "test -w first-dir/w2" '' + dotest_fail devcom2-11 "test -w first-dir/w3" '' + cd .. + rm -rf 1 2 ${CVSROOT_DIRNAME}/first-dir ;; + devcom3) + # More watch tests, most notably handling of features designed + # for future expansion. + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest devcom3-1 "${testcvs} -q co first-dir" '' + cd first-dir + + touch w1 w2 + dotest devcom3-2 "${testcvs} add w1 w2" "${DOTSTAR}" + dotest devcom3-3 "${testcvs} watch on w1 w2" '' + dotest devcom3-4 "${testcvs} -q ci -m add-them" "${DOTSTAR}" + + # OK, since we are about to delve into CVS's internals, make + # sure that we seem to be correct about how they work. + dotest devcom3-5 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ +"Fw1 _watched= +Fw2 _watched=" + # Now write a few more lines, just as if we were a newer version + # of CVS implementing some new feature. + cat <<EOF >>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr +Enew line here +G@#$^!@#=& +EOF + # Now get CVS to write to the fileattr file.... + dotest devcom3-6 "${testcvs} watch off w1" '' + # ...and make sure that it hasn't clobbered our new lines. + # Note that writing these lines in another order would be OK + # too. + dotest devcom3-7 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ +"Fw2 _watched= +G@#..!@#=& +Enew line here" + + # See what CVS does when a file name is duplicated. The + # behavior of all versions of CVS since file attributes were + # implemented is that it nukes the duplications. This seems + # reasonable enough, although it means it isn't clear how + # useful duplicates would be for purposes of future + # expansion. But in the interests of keeping behaviors + # predictable, might as well test for it, I guess. + echo 'Fw2 duplicate=' >>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr + dotest devcom3-8 "${testcvs} watch on w1" '' + dotest devcom3-9 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ +"Fw2 _watched= +Fw1 _watched= +Enew line here +G@#..!@#=&" + + cd ../.. + + rm -rf 1 ${CVSROOT_DIRNAME}/first-dir + ;; + ignore) - dotest 187a1 "${testcvs} -q co CVSROOT" 'U CVSROOT/modules' + dotest 187a1 "${testcvs} -q co CVSROOT" "U CVSROOT/${DOTSTAR}" cd CVSROOT echo rootig.c >cvsignore dotest 187a2 "${testcvs} add cvsignore" "${PROG}"' [a-z]*: scheduling file `cvsignore'"'"' for addition @@ -2578,13 +4925,13 @@ abc [a-z0-9]* edit unedit commit' # As of Jan 96, local CVS prints "Examining ." and remote doesn't. # Accept either. dotest 187a3 " ${testcvs} ci -m added" \ -"${DOTSTAR}"'CS file: /tmp/cvs-sanity/cvsroot/CVSROOT/cvsignore,v +"${DOTSTAR}CS file: ${TESTDIR}/cvsroot/CVSROOT/cvsignore,v done Checking in cvsignore; -/tmp/cvs-sanity/cvsroot/CVSROOT/cvsignore,v <-- cvsignore -initial revision: 1.1 +${TESTDIR}/cvsroot/CVSROOT/cvsignore,v <-- cvsignore +initial revision: 1\.1 done -'"${PROG}"' [a-z]*: Rebuilding administrative file database' +${PROG} [a-z]*: Rebuilding administrative file database" cd .. if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then @@ -2631,6 +4978,8 @@ No conflicts created by this import' cd .. rm -rf dir-to-import + mkdir 1 + cd 1 dotest 189a "${testcvs} -q co second-dir" \ 'U second-dir/bar.c U second-dir/defig.o @@ -2638,7 +4987,6 @@ U second-dir/envig.c U second-dir/foobar.c U second-dir/optig.c U second-dir/rootig.c' - rm -rf second-dir dotest 189b "${testcvs} -q co first-dir" 'U first-dir/bar.c U first-dir/foobar.c' cd first-dir @@ -2651,9 +4999,78 @@ ${QUESTION} defig.o ${QUESTION} envig.c ${QUESTION} optig.c ${QUESTION} notig.c" - cd .. - rm -rf first-dir + # Now test that commands other than update also print "? notig.c" + # where appropriate. Only test this for remote, because local + # CVS only prints it on update. + rm optig.c + if test "x$remote" = xyes; then + dotest 189e "${testcvs} -q diff" "${QUESTION} notig.c" + + # Force the server to be contacted. Ugh. Having CVS + # contact the server for the sole purpose of checking + # the CVSROOT/cvsignore file does not seem like such a + # good idea, so I imagine this will continue to be + # necessary. Oh well, at least we test CVS's ablity to + # handle a file with a modified timestamp but unmodified + # contents. + touch bar.c + + dotest 189f "${testcvs} -q ci -m commit-it" "${QUESTION} notig.c" + fi + + # now test .cvsignore files + cd .. + echo notig.c >first-dir/.cvsignore + echo foobar.c >second-dir/.cvsignore + touch first-dir/notig.c second-dir/notig.c second-dir/foobar.c + dotest 190 "${testcvs} -qn update" \ +"${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/.cvsignore +${QUESTION} second-dir/notig.c" \ +"${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/notig.c +${QUESTION} second-dir/.cvsignore" + dotest 191 "${testcvs} -qn update -I! -I CVS" \ +"${QUESTION} first-dir/rootig.c +${QUESTION} first-dir/defig.o +${QUESTION} first-dir/envig.c +${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/.cvsignore +${QUESTION} second-dir/notig.c" \ +"${QUESTION} first-dir/rootig.c +${QUESTION} first-dir/defig.o +${QUESTION} first-dir/envig.c +${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/notig.c +${QUESTION} second-dir/.cvsignore" + + if echo yes | ${testcvs} release -d first-dir \ + >${TESTDIR}/ignore.tmp; then + pass ignore-192 + else + fail ignore-192 + fi + dotest ignore-193 "cat ${TESTDIR}/ignore.tmp" \ +"${QUESTION} \.cvsignore +You have \[0\] altered files in this repository. +Are you sure you want to release (and delete) directory .first-dir': " + + echo add a line >>second-dir/foobar.c + rm second-dir/notig.c second-dir/.cvsignore + if echo yes | ${testcvs} release -d second-dir \ + >${TESTDIR}/ignore.tmp; then + pass ignore-194 + else + fail ignore-194 + fi + dotest ignore-195 "cat ${TESTDIR}/ignore.tmp" \ +"M foobar.c +You have \[1\] altered files in this repository. +Are you sure you want to release (and delete) directory .second-dir': " + cd .. + rm -rf 1 + rm ${TESTDIR}/ignore.tmp rm -rf ${CVSROOT_DIRNAME}/first-dir ${CVSROOT_DIRNAME}/second-dir ;; @@ -2671,12 +5088,12 @@ ${QUESTION} notig.c" "${PROG}"' [a-z]*: scheduling file `binfile'\'' for addition '"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' dotest binfiles-3 "${testcvs} -q ci -m add-it" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/binfile,v +"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v done Checking in binfile; -/tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile -initial revision: 1.1 -done' +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +initial revision: 1\.1 +done" cd ../.. mkdir 2; cd 2 dotest binfiles-4 "${testcvs} -q co first-dir" 'U first-dir/binfile' @@ -2686,24 +5103,73 @@ done' # to testing that binary files work right on non-unix machines # (until there is automated testing for such machines, of course). dotest binfiles-5.5 "${testcvs} status binfile" \ -'=================================================================== +"=================================================================== +File: binfile Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/binfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" + + # Test whether the default options from the RCS file are + # also used when operating on files instead of whole + # directories + cd ../.. + mkdir 3; cd 3 + dotest binfiles-5.5b0 "${testcvs} -q co first-dir/binfile" \ +'U first-dir/binfile' + cd first-dir + dotest binfiles-5.5b1 "${testcvs} status binfile" \ +"=================================================================== File: binfile Status: Up-to-date Working revision: 1\.1.* - Repository revision: 1\.1 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/binfile,v Sticky Tag: (none) Sticky Date: (none) - Sticky Options: -kb' + Sticky Options: -kb" + cd ../.. + rm -rf 3 + cd 2/first-dir + cp ../../1/binfile2.dat binfile dotest binfiles-6 "${testcvs} -q ci -m modify-it" \ -'Checking in binfile; -/tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile -new revision: 1.2; previous revision: 1.1 -done' +"Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.2; previous revision: 1\.1 +done" cd ../../1/first-dir dotest binfiles-7 "${testcvs} -q update" '[UP] binfile' dotest binfiles-8 "cmp ../binfile2.dat binfile" '' + # Now test handling of conflicts with binary files. + cp ../binfile.dat binfile + dotest binfiles-con0 "${testcvs} -q ci -m modify-it" \ +"Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.3; previous revision: 1\.2 +done" + cd ../../2/first-dir + echo 'edits in dir 2' >binfile + dotest binfiles-con1 "${testcvs} -q update" \ +"U binfile +${PROG} [a-z]*: binary file needs merge +${PROG} [a-z]*: revision 1\.3 from repository is now in binfile +${PROG} [a-z]*: file from working directory is now in \.#binfile\.1\.2 +C binfile" + dotest binfiles-con2 "cmp binfile ../../1/binfile.dat" '' + dotest binfiles-con3 "cat .#binfile.1.2" 'edits in dir 2' + + cp ../../1/binfile2.dat binfile + dotest binfiles-con4 "${testcvs} -q ci -m resolve-it" \ +"Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.4; previous revision: 1\.3 +done" + cd ../../1/first-dir + dotest binfiles-con5 "${testcvs} -q update" '[UP] binfile' + # The bugs which these test for are apparently not fixed for remote. if test "$remote" = no; then dotest binfiles-9 "${testcvs} -q update -A" '' @@ -2713,79 +5179,225 @@ done' dotest binfiles-13 "${testcvs} -q update -A" '' fi - cd ../../2/first-dir + cd ../.. + rm -rf 1 + + mkdir 3 + cd 3 + dotest binfiles-13a0 "${testcvs} -q co -r HEAD first-dir" \ +'U first-dir/binfile' + cd first-dir + dotest binfiles-13a1 "${testcvs} status binfile" \ +"=================================================================== +File: binfile Status: Up-to-date + + Working revision: 1\.4.* + Repository revision: 1\.4 ${TESTDIR}/cvsroot/first-dir/binfile,v + Sticky Tag: HEAD (revision: 1\.4) + Sticky Date: (none) + Sticky Options: -kb" + cd ../.. + rm -rf 3 + + cd 2/first-dir echo 'this file is $''RCSfile$' >binfile dotest binfiles-14a "${testcvs} -q ci -m modify-it" \ -'Checking in binfile; -/tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile -new revision: 1.3; previous revision: 1.2 -done' +"Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.5; previous revision: 1\.4 +done" dotest binfiles-14b "cat binfile" 'this file is $''RCSfile$' # See binfiles-5.5 for discussion of -kb. dotest binfiles-14c "${testcvs} status binfile" \ -'=================================================================== +"=================================================================== File: binfile Status: Up-to-date - Working revision: 1\.3.* - Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Working revision: 1\.5.* + Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v Sticky Tag: (none) Sticky Date: (none) - Sticky Options: -kb' + Sticky Options: -kb" dotest binfiles-14d "${testcvs} admin -kv binfile" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/binfile,v -done' +"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v +done" # cvs admin doesn't change the checked-out file or its sticky # kopts. There probably should be a way which does (but # what if the file is modified? And do we try to version # control the kopt setting?) dotest binfiles-14e "cat binfile" 'this file is $''RCSfile$' dotest binfiles-14f "${testcvs} status binfile" \ -'=================================================================== +"=================================================================== File: binfile Status: Up-to-date - Working revision: 1\.3.* - Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Working revision: 1\.5.* + Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v Sticky Tag: (none) Sticky Date: (none) - Sticky Options: -kb' + Sticky Options: -kb" dotest binfiles-14g "${testcvs} -q update -A" '[UP] binfile' dotest binfiles-14h "cat binfile" 'this file is binfile,v' dotest binfiles-14i "${testcvs} status binfile" \ -'=================================================================== +"=================================================================== File: binfile Status: Up-to-date - Working revision: 1\.3.* - Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Working revision: 1\.5.* + Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v Sticky Tag: (none) Sticky Date: (none) - Sticky Options: -kv' + Sticky Options: -kv" + + # Do sticky options work when used with 'cvs update'? + echo "Not a binary file." > nibfile + dotest binfiles-sticky1 "${testcvs} -q add nibfile" \ +"${PROG} [a-z]*: use "\''cvs commit'\'' to add this file permanently' + dotest binfiles-sticky2 "${testcvs} -q ci -m add-it nibfile" \ + "RCS file: ${TESTDIR}/cvsroot/first-dir/nibfile,v +done +Checking in nibfile; +${TESTDIR}/cvsroot/first-dir/nibfile,v <-- nibfile +initial revision: 1\.1 +done" + dotest binfiles-sticky3 "${testcvs} -q update -kb nibfile" \ + '[UP] nibfile' + dotest binfiles-sticky4 "${testcvs} -q status nibfile" \ +"=================================================================== +File: nibfile Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/nibfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" + # Eventually we should test that -A removes the -kb here... cd ../.. rm -rf ${CVSROOT_DIRNAME}/first-dir - rm -r 1 2 + rm -r 2 + ;; + + binfiles2) + # Test cvs's ability to handle binary files, particularly branching + # and joining. The key thing we are worrying about is that CVS + # doesn't print "cannot merge binary files" or some such, in + # situations where no merging is required. + # See also "join" which does this with non-binary files. + + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1; cd 1 + dotest binfiles2-1 "${testcvs} -q co first-dir" '' + cd first-dir + # FIXCVS: unless a branch has at least one file on it, + # tag_check_valid won't know it exists. So creating a + # file here is a workaround. + touch dummy + dotest binfiles2-1a "${testcvs} add dummy" \ +"${PROG} [a-z]*: scheduling file .dummy. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest binfiles2-1b "${testcvs} -q ci -m add-it" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/dummy,v +done +Checking in dummy; +${TESTDIR}/cvsroot/first-dir/dummy,v <-- dummy +initial revision: 1\.1 +done" + dotest binfiles2-2 "${testcvs} -q tag -b br" 'T dummy' + dotest binfiles2-3 "${testcvs} -q update -r br" '' + awk 'BEGIN { printf "%c%c%c%c%c%c", 2, 10, 137, 0, 13, 10 }' \ + </dev/null >../binfile + cp ../binfile binfile.dat + dotest binfiles2-4 "${testcvs} add -kb binfile.dat" \ +"${PROG} [a-z]*: scheduling file .binfile\.dat. for addition on branch .br. +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest binfiles2-5 "${testcvs} -q ci -m add-it" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/binfile\.dat,v +done +Checking in binfile\.dat; +${TESTDIR}/cvsroot/first-dir/Attic/binfile\.dat,v <-- binfile\.dat +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + dotest binfiles2-6 "${testcvs} -q update -A" \ +"${PROG} [a-z]*: warning: binfile\.dat is not (any longer) pertinent" + dotest_fail binfiles2-7 "test -f binfile.dat" '' + dotest binfiles2-8 "${testcvs} -q update -j br" "U binfile.dat" + dotest binfiles2-9 "cmp ../binfile binfile.dat" + cd .. + cd .. + + rm -rf ${CVSROOT_DIRNAME}/first-dir + rm -r 1 + ;; + + binwrap) + # Test the ability to specify binary-ness based on file name. + # We could also be testing the ability to use the other + # ways to specify a wrapper (CVSROOT/cvswrappers, etc.). + + mkdir dir-to-import + cd dir-to-import + touch foo.c foo.exe + + # While we're here, test for rejection of duplicate tag names. + dotest_fail binwrap-0 \ + "${testcvs} import -m msg -I ! first-dir dup dup" \ +"${PROG} \[[a-z]* aborted\]: tag .dup. was specified more than once" + + if ${testcvs} import -m message -I ! -W "*.exe -k 'b'" \ + first-dir tag1 tag2 >>${LOGFILE}; then + pass binwrap-1 + else + fail binwrap-1 + fi + cd .. + rm -rf dir-to-import + dotest binwrap-2 "${testcvs} -q co first-dir" 'U first-dir/foo.c +U first-dir/foo.exe' + dotest binwrap-3 "${testcvs} -q status first-dir" \ +"=================================================================== +File: foo\.c Status: Up-to-date + + Working revision: 1\.1\.1\.1.* + Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.c,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: foo\.exe Status: Up-to-date + + Working revision: 1\.1\.1\.1.* + Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.exe,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" + rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir ;; + info) # Test CVS's ability to handle *info files. dotest info-1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" cd CVSROOT echo "ALL sh -c \"echo x\${=MYENV}\${=OTHER}y\${=ZEE}=\$USER=\$CVSROOT= >>$TESTDIR/testlog; cat >/dev/null\"" > loginfo - dotest info-2 "${testcvs} add loginfo" \ + # The following cases test the format string substitution + echo "ALL echo %{sVv} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + echo "ALL echo %{v} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + echo "ALL echo %s >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + echo "ALL echo %{V}AX >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + echo "ALL echo %sux >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + + # Might be nice to move this to crerepos tests; it should + # work to create a loginfo file if you didn't create one + # with "cvs init". + : dotest info-2 "${testcvs} add loginfo" \ "${PROG}"' [a-z]*: scheduling file `loginfo'"'"' for addition '"${PROG}"' [a-z]*: use '"'"'cvs commit'"'"' to add this file permanently' + dotest info-3 "${testcvs} -q ci -m new-loginfo" \ -'RCS file: /tmp/cvs-sanity/cvsroot/CVSROOT/loginfo,v +"Checking in loginfo; +${TESTDIR}/cvsroot/CVSROOT/loginfo,v <-- loginfo +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done -Checking in loginfo; -/tmp/cvs-sanity/cvsroot/CVSROOT/loginfo,v <-- loginfo -initial revision: 1.1 -done -'"${PROG}"' [a-z]*: Rebuilding administrative file database' +${PROG} [a-z]*: Rebuilding administrative file database" cd .. - if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then - pass info-4 - else - fail info-4 - fi mkdir ${CVSROOT_DIRNAME}/first-dir dotest info-5 "${testcvs} -q co first-dir" '' @@ -2796,34 +5408,1161 @@ done '"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' echo "cvs -s OTHER=not-this -s MYENV=env-" >>$HOME/.cvsrc dotest info-6a "${testcvs} -q -s OTHER=value ci -m add-it" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done Checking in file1; -/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 -initial revision: 1.1 +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 done -'"${PROG}"' [a-z]*: loginfo:1: no such user variable ${=ZEE}' +${PROG} [a-z]*: loginfo:1: no such user variable \${=ZEE}" echo line1 >>file1 dotest info-7 "${testcvs} -q -s OTHER=value -s ZEE=z ci -m mod-it" \ -'Checking in file1; -/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 -new revision: 1.2; previous revision: 1.1 -done' +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + cd .. + dotest info-9 "cat $TESTDIR/testlog" "xenv-valueyz=[a-z0-9@][a-z0-9@]*=${TESTDIR}/cvsroot=" + dotest info-10 "cat $TESTDIR/testlog2" 'first-dir file1,NONE,1.1 +first-dir 1.1 +first-dir file1 +first-dir NONEAX +first-dir file1ux +first-dir file1,1.1,1.2 +first-dir 1.2 +first-dir file1 +first-dir 1.1AX +first-dir file1ux' + + cd CVSROOT + echo '# do nothing' >loginfo + dotest info-11 "${testcvs} -q -s ZEE=garbage ci -m nuke-loginfo" \ +"Checking in loginfo; +${TESTDIR}/cvsroot/CVSROOT/loginfo,v <-- loginfo +new revision: 1\.[0-9]; previous revision: 1\.[0-9] +done +${PROG} [a-z]*: Rebuilding administrative file database" + + # Now test verifymsg + cat >${TESTDIR}/vscript <<EOF +#!/bin/sh +if head -1 < \$1 | grep '^BugId:[ ]*[0-9][0-9]*$' > /dev/null; then + exit 0 +else + echo "No BugId found." + exit 1 +fi +EOF + chmod +x ${TESTDIR}/vscript + echo "^first-dir ${TESTDIR}/vscript" >>verifymsg + dotest info-v1 "${testcvs} -q ci -m add-verification" \ +"Checking in verifymsg; +${TESTDIR}/cvsroot/CVSROOT/verifymsg,v <-- verifymsg +new revision: 1\.2; previous revision: 1\.1 +done +${PROG} [a-z]*: Rebuilding administrative file database" + + cd ../first-dir + echo line2 >>file1 + dotest_fail info-v2 "${testcvs} -q ci -m bogus" \ +"No BugId found\. +${PROG} \[[a-z]* aborted\]: Message verification failed" + + cat >${TESTDIR}/comment.tmp <<EOF +BugId: 42 +and many more lines after it +EOF + dotest info-v3 "${testcvs} -q ci -F ${TESTDIR}/comment.tmp" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3; previous revision: 1\.2 +done" + cd .. + mkdir another-dir + cd another-dir + touch file2 + dotest_fail info-v4 \ + "${testcvs} import -m bogus first-dir/another x y" \ +"No BugId found\. +${PROG} \[[a-z]* aborted\]: Message verification failed" + rm file2 + cd .. + rmdir another-dir + + cd CVSROOT + echo '# do nothing' >verifymsg + dotest info-cleanup-verifymsg "${testcvs} -q ci -m nuke-verifymsg" \ +"Checking in verifymsg; +${TESTDIR}/cvsroot/CVSROOT/verifymsg,v <-- verifymsg +new revision: 1\.[0-9]; previous revision: 1\.[0-9] +done +${PROG} [a-z]*: Rebuilding administrative file database" cd .. + + if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then + pass info-cleanup + else + fail info-cleanup + fi if echo "yes" | ${testcvs} release -d first-dir >>${LOGFILE} ; then - pass info-8 + pass info-cleanup-2 + else + fail info-cleanup-2 + fi + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + + serverpatch) + # Test remote CVS handling of unpatchable files. This isn't + # much of a test for local CVS. + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest serverpatch-1 "${testcvs} -q co first-dir" '' + + cd first-dir + + # Add a file with an RCS keyword. + echo '$''Name$' > file1 + echo '1' >> file1 + dotest serverpatch-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + + dotest serverpatch-3 "${testcvs} -q commit -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + + # Tag the file. + dotest serverpatch-4 "${testcvs} -q tag tag file1" 'T file1' + + # Check out a tagged copy of the file. + cd ../.. + mkdir 2 + cd 2 + dotest serverpatch-5 "${testcvs} -q co -r tag first-dir" \ +'U first-dir/file1' + + # Remove the tag. This will leave the tag string in the + # expansion of the Name keyword. + dotest serverpatch-6 "${testcvs} -q update -A" '' + + # Modify and check in the first copy. + cd ../1/first-dir + echo '2' >> file1 + dotest serverpatch-7 "${testcvs} -q ci -mx file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + + # Now update the second copy. When using remote CVS, the + # patch will fail, forcing the file to be refetched. + cd ../../2/first-dir + dotest serverpatch-8 "${testcvs} -q update" \ +'U file1' \ +'P file1 +'"${PROG}"' [a-z]*: checksum failure after patch to ./file1; will refetch +'"${PROG}"' [a-z]*: refetching unpatchable files +U file1' + + cd ../.. + rm -rf 1 2 ${CVSROOT_DIRNAME}/first-dir + ;; + + log) + # Test selecting revisions with cvs log. + # See also log2 tests for more tests. + # See also rcs tests, for -d option to log. + # See also branches-14.3 for logging with a branch off of a branch. + # See also multibranch-14 for logging with several branches off the + # same branchpoint. + + # Check in a file with a few revisions and branches. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest log-1 "${testcvs} -q co first-dir" '' + cd first-dir + echo 'first revision' > file1 + dotest log-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + + dotest log-3 "${testcvs} -q commit -m 1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + + echo 'second revision' > file1 + dotest log-4 "${testcvs} -q ci -m2 file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + + dotest log-5 "${testcvs} -q tag -b branch file1" 'T file1' + + echo 'third revision' > file1 + dotest log-6 "${testcvs} -q ci -m3 file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3; previous revision: 1\.2 +done" + + dotest log-7 "${testcvs} -q update -r branch" '[UP] file1' + + echo 'first branch revision' > file1 + dotest log-8 "${testcvs} -q ci -m1b file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2\.2\.1; previous revision: 1\.2 +done" + + dotest log-9 "${testcvs} -q tag tag file1" 'T file1' + + echo 'second branch revision' > file1 + dotest log-10 "${testcvs} -q ci -m2b file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1 +done" + + # Set up a bunch of shell variables to make the later tests + # easier to describe.= + log_header=" +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.3 +branch: +locks: strict +access list:" + log_tags='symbolic names: + tag: 1\.2\.2\.1 + branch: 1\.2\.0\.2' + log_header2='keyword substitution: kv' + log_dash='---------------------------- +revision' + log_date="date: [0-9/]* [0-9:]*; author: ${username}; state: Exp;" + log_lines=" lines: ${PLUS}1 -1" + log_rev1="${log_dash} 1\.1 +${log_date} +1" + log_rev2="${log_dash} 1\.2 +${log_date}${log_lines} +branches: 1\.2\.2; +2" + log_rev3="${log_dash} 1\.3 +${log_date}${log_lines} +3" + log_rev1b="${log_dash} 1\.2\.2\.1 +${log_date}${log_lines} +1b" + log_rev2b="${log_dash} 1\.2\.2\.2 +${log_date}${log_lines} +2b" + log_trailer='=============================================================================' + + # Now, finally, test the log output. + + dotest log-11 "${testcvs} log file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 5 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_rev2b} +${log_rev1b} +${log_trailer}" + + dotest log-12 "${testcvs} log -N file1" \ +"${log_header} +${log_header2} +total revisions: 5; selected revisions: 5 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_rev2b} +${log_rev1b} +${log_trailer}" + + dotest log-13 "${testcvs} log -b file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 3 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_trailer}" + + dotest log-14 "${testcvs} log -r file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev3} +${log_trailer}" + + dotest log-15 "${testcvs} log -r1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2} +${log_trailer}" + + dotest log-16 "${testcvs} log -r1.2.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2b} +${log_rev1b} +${log_trailer}" + + # This test would fail with the old invocation of rlog, but it + # works with the builtin log support. + dotest log-17 "${testcvs} log -rbranch file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2b} +${log_rev1b} +${log_trailer}" + + dotest log-18 "${testcvs} log -r1.2.2. file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2b} +${log_trailer}" + + # This test would fail with the old invocation of rlog, but it + # works with the builtin log support. + dotest log-19 "${testcvs} log -rbranch. file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2b} +${log_trailer}" + + dotest log-20 "${testcvs} log -r1.2: file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev3} +${log_rev2} +${log_trailer}" + + dotest log-21 "${testcvs} log -r:1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2} +${log_rev1} +${log_trailer}" + + dotest log-22 "${testcvs} log -r1.1:1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2} +${log_rev1} +${log_trailer}" + + cd .. + rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir + ;; + + log2) + # More "cvs log" tests, for example the file description. + + # Setting the file description doesn't yet work client/server, so + # skip these tests for remote. + if test "x$remote" = xno; then + + # Check in a file + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest log2-1 "${testcvs} -q co first-dir" '' + cd first-dir + echo 'first revision' > file1 + dotest log2-2 "${testcvs} add -m file1-is-for-testing file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest log2-3 "${testcvs} -q commit -m 1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + dotest log2-4 "${testcvs} log -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +keyword substitution: kv +total revisions: 1; selected revisions: 1 +description: +file1-is-for-testing +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +1 +=============================================================================" + + cd .. + rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir + + fi # end of tests skipped for remote + + ;; + crerepos) + # Various tests relating to creating repositories, operating + # on repositories created with old versions of CVS, etc. + + # Because this test is all about -d options and such, it + # at least to some extent needs to be different for remote vs. + # local. + if test "x$remote" = "xno"; then + + # First, if the repository doesn't exist at all... + dotest_fail crerepos-1 \ +"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \ +"${PROG} \[[a-z]* aborted\]: ${TESTDIR}/crerepos/CVSROOT: .*" + mkdir crerepos + + # The repository exists but CVSROOT doesn't. + dotest_fail crerepos-2 \ +"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \ +"${PROG} \[[a-z]* aborted\]: ${TESTDIR}/crerepos/CVSROOT: .*" + mkdir crerepos/CVSROOT + + # Checkout of nonexistent module + dotest_fail crerepos-3 \ +"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \ +"${PROG} [a-z]*: cannot find module .cvs-sanity. - ignored" + + # Now test that CVS works correctly without a modules file + # or any of that other stuff. In particular, it *must* + # function if administrative files added to CVS recently (since + # CVS 1.3) do not exist, because the repository might have + # been created with an old version of CVS. + mkdir tmp; cd tmp + dotest crerepos-4 \ +"${testcvs} -q -d ${TESTDIR}/crerepos co CVSROOT" \ +'' + if echo yes | \ +${testcvs} -d ${TESTDIR}/crerepos release -d CVSROOT >>${LOGFILE}; then + pass crerepos-5 + else + fail crerepos-5 + fi + rm -rf CVS + cd .. + # The directory tmp should be empty + dotest crerepos-6 "rmdir tmp" '' + + else + # For remote, just create the repository. We don't yet do + # the various other tests above for remote but that should be + # changed. + mkdir crerepos + mkdir crerepos/CVSROOT + fi + + if test "x$remote" = "xno"; then + # Test that CVS rejects a relative path in CVSROOT. + mkdir 1; cd 1 + dotest_fail crerepos-6a "${testcvs} -q -d ../crerepos get ." \ +"${PROG} \[[a-z]* aborted\]: CVSROOT ../crerepos must be an absolute pathname" + cd .. + rm -rf 1 + + mkdir 1; cd 1 + dotest_fail crerepos-6b "${testcvs} -d crerepos init" \ +"${PROG} \[[a-z]* aborted\]: CVSROOT crerepos must be an absolute pathname" + cd .. + rm -rf 1 + else # remote + # Test that CVS rejects a relative path in CVSROOT. + mkdir 1; cd 1 + dotest_fail crerepos-6a \ +"${testcvs} -q -d :ext:`hostname`:../crerepos get ." \ +"Root ../crerepos must be an absolute pathname" + cd .. + rm -rf 1 + + mkdir 1; cd 1 + dotest_fail crerepos-6b \ +"${testcvs} -d :ext:`hostname`:crerepos init" \ +"Root crerepos must be an absolute pathname" + cd .. + rm -rf 1 + fi # end of tests to be skipped for remote + + # CVS better not create a history file--if the administrator + # doesn't need it and wants to save on disk space, they just + # delete it. + dotest_fail crerepos-7 \ +"test -f ${TESTDIR}/crerepos/CVSROOT/history" '' + + ;; + + rcs) + # Test ability to import an RCS file. Note that this format + # is fixed--files written by RCS5, and other software which + # implements this format, will be out there "forever" and + # CVS must always be able to import such files. + + # TODO: would be nice to have a corresponding test for exporting + # RCS files. Rather than try to write a rigorous check for whether + # the file CVS exports is legal, we could just write a simpler test + # for what CVS actually exports, and revise the check as needed. + + mkdir ${CVSROOT_DIRNAME}/first-dir + + # Currently the way to import an RCS file is to copy it + # directly into the repository. + # This file was written by RCS 5.7, and then the dates were + # hacked so that we test year 2000 stuff. Note also that + # "author" names are just strings, as far as importing + # RCS files is concerned--they need not correspond to user + # IDs on any particular system. + cat <<EOF >${CVSROOT_DIRNAME}/first-dir/file1,v +head 1.3; +access; +symbols; +locks; strict; +comment @# @; + + +1.3 +date 2000.11.24.15.58.37; author kingdon; state Exp; +branches; +next 1.2; + +1.2 +date 96.11.24.15.57.41; author kingdon; state Exp; +branches; +next 1.1; + +1.1 +date 96.11.24.15.56.05; author kingdon; state Exp; +branches; +next ; + + +desc +@file1 is for testing CVS +@ + + +1.3 +log +@delete second line; modify twelfth line +@ +text +@This is the first line +This is the third line +This is the fourth line +This is the fifth line +This is the sixth line +This is the seventh line +This is the eighth line +This is the ninth line +This is the tenth line +This is the eleventh line +This is the twelfth line (and what a line it is) +This is the thirteenth line +@ + + +1.2 +log +@add more lines +@ +text +@a1 1 +This is the second line +d11 1 +a11 1 +This is the twelfth line +@ + + +1.1 +log +@add file1 +@ +text +@d2 12 +@ +EOF + dotest rcs-1 "${testcvs} -q co first-dir" 'U first-dir/file1' + cd first-dir + dotest rcs-2 "${testcvs} -q log" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.3 +branch: +locks: strict +access list: +symbolic names: +keyword substitution: kv +total revisions: 3; selected revisions: 3 +description: +file1 is for testing CVS +---------------------------- +revision 1\.3 +date: 2000/11/24 15:58:37; author: kingdon; state: Exp; lines: ${PLUS}1 -2 +delete second line; modify twelfth line +---------------------------- +revision 1\.2 +date: 1996/11/24 15:57:41; author: kingdon; state: Exp; lines: ${PLUS}12 -0 +add more lines +---------------------------- +revision 1\.1 +date: 1996/11/24 15:56:05; author: kingdon; state: Exp; +add file1 +=============================================================================" + + # Note that the dates here are chosen so that (a) we test + # at least one date after 2000, (b) we will notice if the + # month and day are getting mixed up with each other. + # TODO: also test that year isn't getting mixed up with month + # or day, for example 01-02-03. + + # ISO8601 format. There are many, many, other variations + # specified by ISO8601 which we should be testing too. + dotest rcs-3 "${testcvs} -q log -d 1996-12-11<" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.3 +branch: +locks: strict +access list: +symbolic names: +keyword substitution: kv +total revisions: 3; selected revisions: 1 +description: +file1 is for testing CVS +---------------------------- +revision 1\.3 +date: 2000/11/24 15:58:37; author: kingdon; state: Exp; lines: ${PLUS}1 -2 +delete second line; modify twelfth line +=============================================================================" + + # RFC822 format (as amended by RFC1123). + if ${testcvs} -q log -d '<3 Apr 2000 00:00' >${TESTDIR}/rcs4.tmp + then + dotest rcs-4 "cat ${TESTDIR}/rcs4.tmp" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.3 +branch: +locks: strict +access list: +symbolic names: +keyword substitution: kv +total revisions: 3; selected revisions: 2 +description: +file1 is for testing CVS +---------------------------- +revision 1\.2 +date: 1996/11/24 15:57:41; author: kingdon; state: Exp; lines: ${PLUS}12 -0 +add more lines +---------------------------- +revision 1\.1 +date: 1996/11/24 15:56:05; author: kingdon; state: Exp; +add file1 +=============================================================================" else - fail info-8 + fail rcs-4 fi - dotest info-9 "cat $TESTDIR/testlog" 'xenv-valueyz=[a-z@][a-z@]*=/tmp/cvs-sanity/cvsroot=' - # I think this might be doable with cvs remove, or at least - # checking in a version with only comments, but I'm too lazy - # at the moment. Blow it away. - rm -f ${CVSROOT_DIRNAME}/CVSROOT/loginfo* + rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir ${TESTDIR}/rcs4.tmp + ;; + + big) + + # Test ability to operate on big files. Intention is to + # test various realloc'ing code in RCS_deltas, rcsgetkey, + # etc. "big" is currently defined to be 1000 lines (64000 + # bytes), which in terms of files that users will use is not + # large, merely average, but my reasoning is that this + # should be big enough to make sure realloc'ing is going on + # and that raising it a lot would start to stress resources + # on machines which run the tests, without any significant + # benefit. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest big-1 "${testcvs} -q co first-dir" '' + cd first-dir + for i in 0 1 2 3 4 5 6 7 8 9; do + for j in 0 1 2 3 4 5 6 7 8 9; do + for k in 0 1 2 3 4 5 6 7 8 9; do + echo \ +"This is line ($i,$j,$k) which goes into the file file1 for testing" >>file1 + done + done + done + dotest big-2 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest big-3 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + cd .. + rm -rf first-dir + dotest big-4 "${testcvs} -q get first-dir" "U first-dir/file1" + + if test "$keep" = yes; then + echo Keeping ${TESTDIR} and exiting due to --keep + exit 0 + fi + + rm -rf first-dir rm -rf ${CVSROOT_DIRNAME}/first-dir ;; + + modes) + # Test repository permissions (CVSUMASK and so on). + # Although the tests in this section "cheat" by testing + # repository permissions, which are sort of not a user-visible + # sort of thing, the modes do have user-visible consequences, + # such as whether a second user can check out the files. But + # it would be awkward to test the consequences, so we don't. + + # Solaris /bin/sh doesn't support export -n. I'm not sure + # what we can do about this, other than hope that whoever + # is running the tests doesn't have CVSUMASK set. + #export -n CVSUMASK # if unset, defaults to 002 + + umask 077 + mkdir 1; cd 1 + dotest modes-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest modes-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + touch aa + dotest modes-3 "${testcvs} add aa" \ +"${PROG} [a-z]*: scheduling file .aa. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest modes-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aa,v +done +Checking in aa; +${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +initial revision: 1\.1 +done" + dotest modes-5 "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ +"-r--r--r-- .*" + + # Test for whether we can set the execute bit. + chmod +x aa + echo change it >>aa + dotest modes-6 "${testcvs} -q ci -m set-execute-bit" \ +"Checking in aa; +${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +new revision: 1\.2; previous revision: 1\.1 +done" + # If CVS let us update the execute bit, it would be set here. + # But it doesn't, and as far as I know that is longstanding + # CVS behavior. + dotest modes-7 "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ +"-r--r--r-- .*" + + # OK, now manually change the modes and see what happens. + chmod g=r,o= ${TESTDIR}/cvsroot/first-dir/aa,v + echo second line >>aa + dotest modes-7a "${testcvs} -q ci -m set-execute-bit" \ +"Checking in aa; +${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +new revision: 1\.3; previous revision: 1\.2 +done" + dotest modes-7b "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ +"-r--r----- .*" + + CVSUMASK=007 + export CVSUMASK + touch ab + # Might as well test the execute bit too. + chmod +x ab + dotest modes-8 "${testcvs} add ab" \ +"${PROG} [a-z]*: scheduling file .ab. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest modes-9 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/ab,v +done +Checking in ab; +${TESTDIR}/cvsroot/first-dir/ab,v <-- ab +initial revision: 1\.1 +done" + if test "x$remote" = xyes; then + # The problem here is that the CVSUMASK environment variable + # needs to be set on the server (e.g. .bashrc). This is, of + # course, bogus, but that is the way it is currently. + dotest modes-10 "ls -l ${TESTDIR}/cvsroot/first-dir/ab,v" \ +"-r-xr-x---.*" "-r-xr-xr-x.*" + else + dotest modes-10 "ls -l ${TESTDIR}/cvsroot/first-dir/ab,v" \ +"-r-xr-x---.*" + fi + + # OK, now add a file on a branch. Check that the mode gets + # set the same way (it is a different code path in CVS). + dotest modes-11 "${testcvs} -q tag -b br" 'T aa +T ab' + dotest modes-12 "${testcvs} -q update -r br" '' + touch ac + dotest modes-13 "${testcvs} add ac" \ +"${PROG} [a-z]*: scheduling file .ac. for addition on branch .br. +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + # Not sure it really makes sense to refer to a "previous revision" + # when we are just now adding the file; as far as I know + # that is longstanding CVS behavior, for what it's worth. + dotest modes-14 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/ac,v +done +Checking in ac; +${TESTDIR}/cvsroot/first-dir/Attic/ac,v <-- ac +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + if test "x$remote" = xyes; then + # The problem here is that the CVSUMASK environment variable + # needs to be set on the server (e.g. .bashrc). This is, of + # course, bogus, but that is the way it is currently. + dotest modes-15 \ +"ls -l ${TESTDIR}/cvsroot/first-dir/Attic/ac,v" \ +"-r--r--r--.*" + else + dotest modes-15 \ +"ls -l ${TESTDIR}/cvsroot/first-dir/Attic/ac,v" \ +"-r--r-----.*" + fi + + cd ../.. + rm -rf 1 ${CVSROOT_DIRNAME}/first-dir + # Perhaps should restore the umask and CVSUMASK. But the other + # tests "should" not care about them... + ;; + + sticky) + # More tests of sticky tags, particularly non-branch sticky tags. + mkdir 1; cd 1 + dotest sticky-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest sticky-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + + touch file1 + dotest sticky-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest sticky-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + dotest sticky-5 "${testcvs} -q tag tag1" "T file1" + echo add a line >>file1 + dotest sticky-6 "${testcvs} -q ci -m modify" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + dotest sticky-7 "${testcvs} -q update -r tag1" "[UP] file1" + dotest sticky-8 "cat file1" '' + dotest sticky-9 "${testcvs} -q update" '' + dotest sticky-10 "cat file1" '' + touch file2 + dotest_fail sticky-11 "${testcvs} add file2" \ +"${PROG} [a-z]*: cannot add file on non-branch tag tag1" + dotest sticky-12 "${testcvs} -q update -A" "[UP] file1 +${QUESTION} file2" "${QUESTION} file2 +[UP] file1" + dotest sticky-13 "${testcvs} add file2" \ +"${PROG} [a-z]*: scheduling file .file2. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + + cd ../.. + rm -rf 1 ${CVSROOT_DIRNAME}/first-dir + ;; + + keyword) + # Test keyword expansion. + mkdir 1; cd 1 + dotest keyword-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest keyword-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + + echo '$''Author$' > file1 + echo '$''Date$' >> file1 + echo '$''Header$' >> file1 + echo '$''Id$' >> file1 + echo '$''Locker$' >> file1 + echo '$''Name$' >> file1 + echo '$''RCSfile$' >> file1 + echo '$''Revision$' >> file1 + echo '$''Source$' >> file1 + echo '$''State$' >> file1 + echo '$''Nonkey$' >> file1 + # Omit the trailing dollar sign + echo '$''Date' >> file1 + # Put two keywords on one line + echo '$''State$' '$''State$' >> file1 + # Use a header for Log + echo 'xx $''Log$' >> file1 + + dotest keyword-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest keyword-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + dotest keyword-5 "cat file1" \ +'\$'"Author: ${username} "'\$'" +"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" +"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" +"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" +"'\$'"Locker: "'\$'" +"'\$'"Name: "'\$'" +"'\$'"RCSfile: file1,v "'\$'" +"'\$'"Revision: 1\.1 "'\$'" +"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" +"'\$'"State: Exp "'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" +xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" + + # Use cvs admin to lock the RCS file in order to check -kkvl + # vs. -kkv. CVS does not normally lock RCS files, but some + # people use cvs admin to enforce reserved checkouts. + dotest keyword-6 "${testcvs} admin -l file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +1\.1 locked +done" + + dotest keyword-7 "${testcvs} update -kkv file1" "U file1" + dotest keyword-8 "cat file1" \ +'\$'"Author: ${username} "'\$'" +"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" +"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" +"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" +"'\$'"Locker: "'\$'" +"'\$'"Name: "'\$'" +"'\$'"RCSfile: file1,v "'\$'" +"'\$'"Revision: 1\.1 "'\$'" +"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" +"'\$'"State: Exp "'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" +xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" + + dotest keyword-9 "${testcvs} update -kkvl file1" "U file1" + dotest keyword-10 "cat file1" \ +'\$'"Author: ${username} "'\$'" +"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" +"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp ${username} "'\$'" +"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp ${username} "'\$'" +"'\$'"Locker: ${username} "'\$'" +"'\$'"Name: "'\$'" +"'\$'"RCSfile: file1,v "'\$'" +"'\$'"Revision: 1\.1 "'\$'" +"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" +"'\$'"State: Exp "'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" +xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" + + dotest keyword-11 "${testcvs} update -kk file1" "U file1" + dotest keyword-12 "cat file1" \ +'\$'"Author"'\$'" +"'\$'"Date"'\$'" +"'\$'"Header"'\$'" +"'\$'"Id"'\$'" +"'\$'"Locker"'\$'" +"'\$'"Name"'\$'" +"'\$'"RCSfile"'\$'" +"'\$'"Revision"'\$'" +"'\$'"Source"'\$'" +"'\$'"State"'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State"'\$'" "'\$'"State"'\$'" +xx "'\$'"Log"'\$'" +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" + + dotest keyword-13 "${testcvs} update -kv file1" "U file1" + dotest keyword-14 "cat file1" \ +"${username} +[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] +${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp +file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp + + +file1,v +1\.1 +${TESTDIR}/cvsroot/first-dir/file1,v +Exp +"'\$'"Nonkey"'\$'" +"'\$'"Date +Exp Exp +xx file1,v +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" + + dotest keyword-15 "${testcvs} update -ko file1" "U file1" + dotest keyword-16 "cat file1" \ +'\$'"Author"'\$'" +"'\$'"Date"'\$'" +"'\$'"Header"'\$'" +"'\$'"Id"'\$'" +"'\$'"Locker"'\$'" +"'\$'"Name"'\$'" +"'\$'"RCSfile"'\$'" +"'\$'"Revision"'\$'" +"'\$'"Source"'\$'" +"'\$'"State"'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State"'\$'" "'\$'"State"'\$'" +xx "'\$'"Log"'\$' + + # Test the Name keyword. First go back to normal expansion. + + # FIXME: When using remote, update -A does not revert the + # keyword expansion mode. We work around that bug here. + # This workaround should be removed when the bug is fixed. + if test "x$remote" = "xyes"; then + cd .. + rm -rf first-dir + dotest keyword-17 "${testcvs} -q co first-dir" "U first-dir/file1" + cd first-dir + else + dotest keyword-17 "${testcvs} update -A file1" "U file1" + fi + + echo '$''Name$' > file1 + dotest keyword-18 "${testcvs} ci -m modify file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + dotest keyword-19 "${testcvs} -q tag tag1" "T file1" + echo "change" >> file1 + dotest keyword-20 "${testcvs} -q ci -m mod2 file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3; previous revision: 1\.2 +done" + dotest keyword-21 "${testcvs} -q update -r tag1" "[UP] file1" + + # FIXME: This test fails when remote. The second expect + # string below should be removed when this is fixed. + dotest keyword-22 "cat file1" '\$'"Name: tag1 "'\$' \ +'\$'"Name: "'\$' + + dotest keyword-23 "${testcvs} update -A file1" "[UP] file1" + + # Test the Log keyword. + echo 'xx $''Log$' > file1 + cat >${TESTDIR}/comment.tmp <<EOF +First log line +Second log line +EOF + dotest keyword-24 "${testcvs} ci -F ${TESTDIR}/comment.tmp file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.4; previous revision: 1\.3 +done" + rm -f ${TESTDIR}/comment.tmp + dotest keyword-25 "cat file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.4 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx" + + echo "change" >> file1 + dotest keyword-26 "${testcvs} ci -m modify file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.5; previous revision: 1\.4 +done" + dotest keyword-27 "cat file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.5 [0-9/]* [0-9:]* ${username} +xx modify +xx +xx Revision 1\.4 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx +change" + + cd ../.. + rm -rf 1 ${CVSROOT_DIRNAME}/first-dir + ;; + *) echo $what is not the name of a test -- ignored ;; @@ -2833,33 +6572,36 @@ done echo "OK, all tests completed." # TODO: -# * Test `cvs admin'. +# * use "test" not "[" and see if all test's support `-z' # * Test `cvs update -d foo' (where foo does not exist). -# * Test `cvs update foo bar' (where foo and bar are both from the same -# repository). Suppose one is a branch--make sure that both directories -# get updated with the respective correct thing. +# * Test `cvs update foo bar' (where foo and bar are both from the +# same directory in the repository). Suppose one is a branch--make +# sure that both directories get updated with the respective correct +# thing. # * `cvs update ../foo'. Also ../../foo ./../foo foo/../../bar /foo/bar # foo/.././../bar foo/../bar etc. # * Test all flags in modules file. # Test that ciprog gets run both on checkin in that directory, or a # higher-level checkin which recurses into it. -# * Test that $ followed by "Header" followed by $ gets expanded on checkin. -# * Test operations on a directory that contains other directories but has +# * More tests of keyword expansion. +# * Test operations on a directory that contains other directories but has # no files of its own. # * -t global option # * cvs rm followed by cvs add or vice versa (with no checkin in between). # * cvs rm twice (should be a nice error message). # * -P option to checkout--(a) refrains from checking out new empty dirs, # (b) prunes empty dirs already there. -# * Test that cvs -d `hostname`:/tmp/cvs-sanity/non/existent co foo -# gives an appropriate error (e.g. -# Cannot access /tmp/cvs-sanity/non-existent/CVSROOT +# * Test that cvs -d `hostname`:${TESTDIR}/non/existent co foo +# gives an appropriate error (e.g. +# Cannot access ${TESTDIR}/non-existent/CVSROOT # No such file or directory). +# (like basica-9, but for remote). # * Test ability to send notifications in response to watches. (currently # hard to test because CVS doesn't send notifications if username is the # same). # * Test that remote edit and/or unedit works when disconnected from # server (e.g. set CVS_SERVER to "foobar"). +# * Test things to do with the CVS/* files, esp. CVS/Root.... # End of TODO list. # Remove the test directory, but first change out of it. diff --git a/contrib/cvs/src/server.c b/contrib/cvs/src/server.c index e92445b..65b1dd3 100644 --- a/contrib/cvs/src/server.c +++ b/contrib/cvs/src/server.c @@ -1,31 +1,81 @@ +/* This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + #include <assert.h> #include "cvs.h" #include "watch.h" #include "edit.h" #include "fileattr.h" +#include "getline.h" +#include "buffer.h" #ifdef SERVER_SUPPORT +#ifdef HAVE_WINSOCK_H +#include <winsock.h> +#endif + +#if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) +#include <sys/socket.h> +#endif + +#ifdef HAVE_KERBEROS +#include <netinet/in.h> +#include <krb.h> +#ifndef HAVE_KRB_GET_ERR_TEXT +#define krb_get_err_text(status) krb_err_txt[status] +#endif + +/* Information we need if we are going to use Kerberos encryption. */ +static C_Block kblock; +static Key_schedule sched; + +#endif + /* for select */ #include <sys/types.h> #ifdef HAVE_SYS_BSDTYPES_H #include <sys/bsdtypes.h> #endif -#include <sys/time.h> -#if HAVE_SYS_SELECT_H -#include <sys/select.h> +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif #endif -#if HAVE_FCNTL_H -#include <fcntl.h> +#if HAVE_SYS_SELECT_H +#include <sys/select.h> #endif #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif +/* EWOULDBLOCK is not defined by POSIX, but some BSD systems will + return it, rather than EAGAIN, for nonblocking writes. */ +#ifdef EWOULDBLOCK +#define blocking_error(err) ((err) == EWOULDBLOCK || (err) == EAGAIN) +#else +#define blocking_error(err) ((err) == EAGAIN) +#endif + #ifdef AUTH_SERVER_SUPPORT +#ifdef HAVE_GETSPNAM +#include <shadow.h> +#endif /* For initgroups(). */ #if HAVE_INITGROUPS #include <grp.h> @@ -33,23 +83,28 @@ #endif /* AUTH_SERVER_SUPPORT */ -/* Functions which the server calls. */ -int add PROTO((int argc, char **argv)); -int admin PROTO((int argc, char **argv)); -int checkout PROTO((int argc, char **argv)); -int commit PROTO((int argc, char **argv)); -int diff PROTO((int argc, char **argv)); -int history PROTO((int argc, char **argv)); -int import PROTO((int argc, char **argv)); -int cvslog PROTO((int argc, char **argv)); -int patch PROTO((int argc, char **argv)); -int release PROTO((int argc, char **argv)); -int cvsremove PROTO((int argc, char **argv)); -int rtag PROTO((int argc, char **argv)); -int status PROTO((int argc, char **argv)); -int tag PROTO((int argc, char **argv)); -int update PROTO((int argc, char **argv)); +#ifdef AUTH_SERVER_SUPPORT + +/* The cvs username sent by the client, which might or might not be + the same as the system username the server eventually switches to + run as. CVS_Username gets set iff password authentication is + successful. */ +static char *CVS_Username = NULL; + +/* Used to check that same repos is transmitted in pserver auth and in + later CVS protocol. Exported because root.c also uses. */ +char *Pserver_Repos = NULL; + +#endif /* AUTH_SERVER_SUPPORT */ + +/* While processing requests, this buffer accumulates data to be sent to + the client, and then once we are in do_cvs_command, we use it + for all the data to be sent. */ +static struct buffer *buf_to_net; + +/* This buffer is used to read input from the client. */ +static struct buffer *buf_from_net; /* * This is where we stash stuff we are going to use. Format string @@ -57,64 +112,189 @@ int update PROTO((int argc, char **argv)); */ static char *server_temp_dir; +/* This is the original value of server_temp_dir, before any possible + changes inserted by serve_max_dotdot. */ +static char *orig_server_temp_dir; + /* Nonzero if we should keep the temp directory around after we exit. */ static int dont_delete_temp; -static char no_mem_error; -#define NO_MEM_ERROR (&no_mem_error) - static void server_write_entries PROTO((void)); -/* - * Read a line from the stream "instream" without command line editing. - * - * Action is compatible with "readline", e.g. space for the result is - * malloc'd and should be freed by the caller. - * - * A NULL return means end of file. A return of NO_MEM_ERROR means - * that we are out of memory. - */ -static char *read_line PROTO((FILE *)); +/* All server communication goes through buffer structures. Most of + the buffers are built on top of a file descriptor. This structure + is used as the closure field in a buffer. */ -static char * -read_line (stream) - FILE *stream; +struct fd_buffer { - int c; - char *result; - int input_index = 0; - int result_size = 80; - - fflush (stdout); - result = (char *) malloc (result_size); - if (result == NULL) - return NO_MEM_ERROR; - - while (1) + /* The file descriptor. */ + int fd; + /* Nonzero if the file descriptor is in blocking mode. */ + int blocking; +}; + +static struct buffer *fd_buffer_initialize + PROTO ((int, int, void (*) (struct buffer *))); +static int fd_buffer_input PROTO((void *, char *, int, int, int *)); +static int fd_buffer_output PROTO((void *, const char *, int, int *)); +static int fd_buffer_flush PROTO((void *)); +static int fd_buffer_block PROTO((void *, int)); + +/* Initialize a buffer built on a file descriptor. FD is the file + descriptor. INPUT is nonzero if this is for input, zero if this is + for output. MEMORY is the function to call when a memory error + occurs. */ + +static struct buffer * +fd_buffer_initialize (fd, input, memory) + int fd; + int input; + void (*memory) PROTO((struct buffer *)); +{ + struct fd_buffer *n; + + n = (struct fd_buffer *) xmalloc (sizeof *n); + n->fd = fd; + n->blocking = 1; + return buf_initialize (input ? fd_buffer_input : NULL, + input ? NULL : fd_buffer_output, + input ? NULL : fd_buffer_flush, + fd_buffer_block, + (int (*) PROTO((void *))) NULL, + memory, + n); +} + +/* The buffer input function for a buffer built on a file descriptor. */ + +static int +fd_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + struct fd_buffer *fd = (struct fd_buffer *) closure; + int nbytes; + + if (! fd->blocking) + nbytes = read (fd->fd, data, size); + else { - c = fgetc (stream); - - if (c == EOF) - { - free (result); - return NULL; - } - - if (c == '\n') - break; - - result[input_index++] = c; - while (input_index >= result_size) + /* This case is not efficient. Fortunately, I don't think it + ever actually happens. */ + nbytes = read (fd->fd, data, need == 0 ? 1 : need); + } + + if (nbytes > 0) + { + *got = nbytes; + return 0; + } + + *got = 0; + + if (nbytes == 0) + { + /* End of file. This assumes that we are using POSIX or BSD + style nonblocking I/O. On System V we will get a zero + return if there is no data, even when not at EOF. */ + return -1; + } + + /* Some error occurred. */ + + if (blocking_error (errno)) + { + /* Everything's fine, we just didn't get any data. */ + return 0; + } + + return errno; +} + +/* The buffer output function for a buffer built on a file descriptor. */ + +static int +fd_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + struct fd_buffer *fd = (struct fd_buffer *) closure; + + *wrote = 0; + + while (have > 0) + { + int nbytes; + + nbytes = write (fd->fd, data, have); + + if (nbytes <= 0) { - result_size *= 2; - result = (char *) realloc (result, result_size); - if (result == NULL) - return NO_MEM_ERROR; + if (! fd->blocking + && (nbytes == 0 || blocking_error (errno))) + { + /* A nonblocking write failed to write any data. Just + return. */ + return 0; + } + + /* Some sort of error occurred. */ + + if (nbytes == 0) + return EIO; + + return errno; } + + *wrote += nbytes; + data += nbytes; + have -= nbytes; } - - result[input_index++] = '\0'; - return result; + + return 0; +} + +/* The buffer flush function for a buffer built on a file descriptor. */ + +/*ARGSUSED*/ +static int +fd_buffer_flush (closure) + void *closure; +{ + /* Nothing to do. File descriptors are always flushed. */ + return 0; +} + +/* The buffer block function for a buffer built on a file descriptor. */ + +static int +fd_buffer_block (closure, block) + void *closure; + int block; +{ + struct fd_buffer *fd = (struct fd_buffer *) closure; + int flags; + + flags = fcntl (fd->fd, F_GETFL, 0); + if (flags < 0) + return errno; + + if (block) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + + if (fcntl (fd->fd, F_SETFL, flags) < 0) + return errno; + + fd->blocking = block; + + return 0; } /* @@ -134,6 +314,8 @@ mkdir_p (dir) if (q == NULL) return ENOMEM; + retval = 0; + /* * Skip over leading slash if present. We won't bother to try to * make '/'. @@ -149,10 +331,12 @@ mkdir_p (dir) q[p - dir] = '\0'; if (CVS_MKDIR (q, 0777) < 0) { - if (errno != EEXIST - && (errno != EACCES || !isdir(q))) + int saved_errno = errno; + + if (saved_errno != EEXIST + && (saved_errno != EACCES || !isdir (q))) { - retval = errno; + retval = saved_errno; goto done; } } @@ -162,8 +346,6 @@ mkdir_p (dir) { if (CVS_MKDIR (dir, 0777) < 0) retval = errno; - else - retval = 0; goto done; } } @@ -176,17 +358,20 @@ mkdir_p (dir) * Print the error response for error code STATUS. The caller is * reponsible for making sure we get back to the command loop without * any further output occuring. + * Must be called only in contexts where it is OK to send output. */ static void print_error (status) int status; { char *msg; - printf ("error "); + buf_output0 (buf_to_net, "error "); msg = strerror (status); if (msg) - printf ("%s", msg); - printf ("\n"); + buf_output0 (buf_to_net, msg); + buf_append_char (buf_to_net, '\n'); + + buf_flush (buf_to_net, 0); } static int pending_error; @@ -196,17 +381,22 @@ static int pending_error; */ static char *pending_error_text; -/* If an error is pending, print it and return 1. If not, return 0. */ +/* If an error is pending, print it and return 1. If not, return 0. + Must be called only in contexts where it is OK to send output. */ static int print_pending_error () { if (pending_error_text) { - printf ("%s\n", pending_error_text); + buf_output0 (buf_to_net, pending_error_text); + buf_append_char (buf_to_net, '\n'); if (pending_error) print_error (pending_error); else - printf ("error \n"); + buf_output0 (buf_to_net, "error \n"); + + buf_flush (buf_to_net, 0); + pending_error = 0; free (pending_error_text); pending_error_text = NULL; @@ -224,8 +414,32 @@ print_pending_error () /* Is an error pending? */ #define error_pending() (pending_error || pending_error_text) + +static int alloc_pending PROTO ((size_t size)); + +/* Allocate SIZE bytes for pending_error_text and return nonzero + if we could do it. */ +static int +alloc_pending (size) + size_t size; +{ + if (error_pending ()) + /* Probably alloc_pending callers will have already checked for + this case. But we might as well handle it if they don't, I + guess. */ + return 0; + pending_error_text = malloc (size); + if (pending_error_text == NULL) + { + pending_error = ENOMEM; + return 0; + } + return 1; +} -int +static int supported_response PROTO ((char *)); + +static int supported_response (name) char *name; { @@ -270,8 +484,24 @@ serve_valid_responses (arg) { if (rs->status == rs_essential) { - printf ("E response `%s' not supported by client\nerror \n", - rs->name); + buf_output0 (buf_to_net, "E response `"); + buf_output0 (buf_to_net, rs->name); + buf_output0 (buf_to_net, "' not supported by client\nerror \n"); + + /* FIXME: This call to buf_flush could conceivably + cause deadlock, as noted in server_cleanup. */ + buf_flush (buf_to_net, 1); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } else if (rs->status == rs_optional) @@ -279,20 +509,30 @@ serve_valid_responses (arg) } } -static int use_dir_and_repos = 0; - static void serve_root (arg) char *arg; { char *env; - extern char *CVSroot; - char path[PATH_MAX]; + char *path; int save_errno; if (error_pending()) return; - - (void) sprintf (path, "%s/%s", arg, CVSROOTADM); + + if (!isabsolute (arg)) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E Root %s must be an absolute pathname", arg); + return; + } + set_local_cvsroot (arg); + + path = xmalloc (strlen (CVSroot_directory) + + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_HISTORY) + + 10); + (void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM); if (!isaccessible (path, R_OK | X_OK)) { save_errno = errno; @@ -312,26 +552,42 @@ serve_root (arg) Sorry, you don't have read/write access to the history file %s", path); pending_error = save_errno; } + free (path); - CVSroot = malloc (strlen (arg) + 1); - if (CVSroot == NULL) - { - pending_error = ENOMEM; - return; - } - strcpy (CVSroot, arg); #ifdef HAVE_PUTENV - env = malloc (strlen (CVSROOT_ENV) + strlen (CVSroot) + 1 + 1); + env = malloc (strlen (CVSROOT_ENV) + strlen (CVSroot_directory) + 1 + 1); if (env == NULL) { pending_error = ENOMEM; return; } - (void) sprintf (env, "%s=%s", CVSROOT_ENV, arg); + (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot_directory); (void) putenv (env); /* do not free env, as putenv has control of it */ #endif } + +static int max_dotdot_limit = 0; + +/* Is this pathname OK to recurse into when we are running as the server? + If not, call error() with a fatal error. */ +void +server_pathname_check (path) + char *path; +{ + /* An absolute pathname is almost surely a path on the *client* machine, + and is unlikely to do us any good here. It also is probably capable + of being a security hole in the anonymous readonly case. */ + if (isabsolute (path)) + error (1, 0, "absolute pathname `%s' illegal for server", path); + if (pathname_levels (path) > max_dotdot_limit) + { + /* Similar to the isabsolute case in security implications. */ + error (0, 0, "protocol error: `%s' contains more leading ..", path); + error (1, 0, "than the %d which Max-dotdot specified", + max_dotdot_limit); + } +} /* * Add as many directories to the temp directory as the client tells us it @@ -357,8 +613,10 @@ serve_max_dotdot (arg) strcpy (p, server_temp_dir); for (i = 0; i < lim; ++i) strcat (p, "/d"); - free (server_temp_dir); + if (server_temp_dir != orig_server_temp_dir) + free (server_temp_dir); server_temp_dir = p; + max_dotdot_limit = lim; } static char *dir_name; @@ -370,6 +628,7 @@ dirswitch (dir, repos) { int status; FILE *f; + char *b; server_write_entries (); @@ -378,6 +637,19 @@ dirswitch (dir, repos) if (dir_name != NULL) free (dir_name); + /* Check for a trailing '/'. This is not ISDIRSEP because \ in the + protocol is an ordinary character, not a directory separator (of + course, it is perhaps unwise to use it in directory names, but that + is another issue). */ + if (strlen (dir) > 0 + && dir[strlen (dir) - 1] == '/') + { + if (alloc_pending (80 + strlen (dir))) + sprintf (pending_error_text, + "E protocol error: illegal directory syntax in %s", dir); + return; + } + dir_name = malloc (strlen (server_temp_dir) + strlen (dir) + 40); if (dir_name == NULL) { @@ -394,15 +666,21 @@ dirswitch (dir, repos) && status != EEXIST) { pending_error = status; - pending_error_text = malloc (80 + strlen(dir_name)); - sprintf(pending_error_text, "E cannot mkdir %s", dir_name); + if (alloc_pending (80 + strlen (dir_name))) + sprintf (pending_error_text, "E cannot mkdir %s", dir_name); return; } - if (chdir (dir_name) < 0) + + b = strrchr (dir_name, '/'); + *b = '\0'; + Subdir_Register ((List *) NULL, dir_name, b + 1); + *b = '/'; + + if ( CVS_CHDIR (dir_name) < 0) { pending_error = errno; - pending_error_text = malloc (80 + strlen(dir_name)); - sprintf(pending_error_text, "E cannot change to %s", dir_name); + if (alloc_pending (80 + strlen (dir_name))) + sprintf (pending_error_text, "E cannot change to %s", dir_name); return; } /* @@ -417,13 +695,33 @@ dirswitch (dir, repos) pending_error = errno; return; } - f = fopen (CVSADM_REP, "w"); + f = CVS_FOPEN (CVSADM_REP, "w"); if (f == NULL) { pending_error = errno; return; } - if (fprintf (f, "%s\n", repos) < 0) + if (fprintf (f, "%s", repos) < 0) + { + pending_error = errno; + fclose (f); + return; + } + /* Non-remote CVS handles a module representing the entire tree + (e.g., an entry like ``world -a .'') by putting /. at the end + of the Repository file, so we do the same. */ + if (strcmp (dir, ".") == 0 + && CVSroot_directory != NULL + && strcmp (CVSroot_directory, repos) == 0) + { + if (fprintf (f, "/.") < 0) + { + pending_error = errno; + fclose (f); + return; + } + } + if (fprintf (f, "\n") < 0) { pending_error = errno; fclose (f); @@ -434,19 +732,21 @@ dirswitch (dir, repos) pending_error = errno; return; } - f = fopen (CVSADM_ENT, "w+"); + /* We open in append mode because we don't want to clobber an + existing Entries file. */ + f = CVS_FOPEN (CVSADM_ENT, "a"); if (f == NULL) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_ENT)); - sprintf(pending_error_text, "E cannot open %s", CVSADM_ENT); + if (alloc_pending (80 + strlen (CVSADM_ENT))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_ENT); return; } if (fclose (f) == EOF) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_ENT)); - sprintf(pending_error_text, "E cannot close %s", CVSADM_ENT); + if (alloc_pending (80 + strlen (CVSADM_ENT))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_ENT); return; } } @@ -455,42 +755,50 @@ static void serve_repository (arg) char *arg; { - dirswitch (arg + 1, arg); + pending_error_text = malloc (80); + if (pending_error_text == NULL) + pending_error = ENOMEM; + else + strcpy (pending_error_text, + "E Repository request is obsolete; aborted"); + return; } static void serve_directory (arg) char *arg; { + int status; char *repos; - use_dir_and_repos = 1; - repos = read_line (stdin); - if (repos == NULL) + + status = buf_read_line (buf_from_net, &repos, (int *) NULL); + if (status == 0) { - pending_error_text = malloc (80 + strlen (arg)); - if (pending_error_text) - { - if (feof (stdin)) - sprintf (pending_error_text, - "E end of file reading mode for %s", arg); - else - { - sprintf (pending_error_text, - "E error reading mode for %s", arg); - pending_error = errno; - } - } - else - pending_error = ENOMEM; + dirswitch (arg, repos); + free (repos); } - else if (repos == NO_MEM_ERROR) + else if (status == -2) { - pending_error = ENOMEM; + pending_error = ENOMEM; } else { - dirswitch (arg, repos); - free (repos); + pending_error_text = malloc (80 + strlen (arg)); + if (pending_error_text == NULL) + { + pending_error = ENOMEM; + } + else if (status == -1) + { + sprintf (pending_error_text, + "E end of file reading mode for %s", arg); + } + else + { + sprintf (pending_error_text, + "E error reading mode for %s", arg); + pending_error = status; + } } } @@ -499,19 +807,22 @@ serve_static_directory (arg) char *arg; { FILE *f; - f = fopen (CVSADM_ENTSTAT, "w+"); + + if (error_pending ()) return; + + f = CVS_FOPEN (CVSADM_ENTSTAT, "w+"); if (f == NULL) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_ENTSTAT)); - sprintf(pending_error_text, "E cannot open %s", CVSADM_ENTSTAT); + if (alloc_pending (80 + strlen (CVSADM_ENTSTAT))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_ENTSTAT); return; } if (fclose (f) == EOF) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_ENTSTAT)); - sprintf(pending_error_text, "E cannot close %s", CVSADM_ENTSTAT); + if (alloc_pending (80 + strlen (CVSADM_ENTSTAT))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_ENTSTAT); return; } } @@ -521,32 +832,35 @@ serve_sticky (arg) char *arg; { FILE *f; - f = fopen (CVSADM_TAG, "w+"); + + if (error_pending ()) return; + + f = CVS_FOPEN (CVSADM_TAG, "w+"); if (f == NULL) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_TAG)); - sprintf(pending_error_text, "E cannot open %s", CVSADM_TAG); + if (alloc_pending (80 + strlen (CVSADM_TAG))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_TAG); return; } if (fprintf (f, "%s\n", arg) < 0) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_TAG)); - sprintf(pending_error_text, "E cannot write to %s", CVSADM_TAG); + if (alloc_pending (80 + strlen (CVSADM_TAG))) + sprintf (pending_error_text, "E cannot write to %s", CVSADM_TAG); return; } if (fclose (f) == EOF) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_TAG)); - sprintf(pending_error_text, "E cannot close %s", CVSADM_TAG); + if (alloc_pending (80 + strlen (CVSADM_TAG))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_TAG); return; } } /* - * Read SIZE bytes from stdin, write them to FILE. + * Read SIZE bytes from buf_from_net, write them to FILE. * * Currently this isn't really used for receiving parts of a file -- * the file is still sent over in one chunk. But if/when we get @@ -559,62 +873,67 @@ receive_partial_file (size, file) int size; int file; { - char buf[16*1024], *bufp; - int toread, nread, nwrote; while (size > 0) { - toread = sizeof (buf); - if (toread > size) - toread = size; + int status, nread; + char *data; - nread = fread (buf, 1, toread, stdin); - if (nread <= 0) + status = buf_read_data (buf_from_net, size, &data, &nread); + if (status != 0) { - if (feof (stdin)) + if (status == -2) + pending_error = ENOMEM; + else { pending_error_text = malloc (80); - if (pending_error_text) + if (pending_error_text == NULL) + pending_error = ENOMEM; + else if (status == -1) { sprintf (pending_error_text, "E premature end of file from client"); pending_error = 0; } else - pending_error = ENOMEM; - } - else if (ferror (stdin)) - { - pending_error_text = malloc (40); - if (pending_error_text) + { sprintf (pending_error_text, "E error reading from client"); - pending_error = errno; - } - else - { - pending_error_text = malloc (40); - if (pending_error_text) - sprintf (pending_error_text, - "E short read from client"); - pending_error = 0; + pending_error = status; + } } return; } + size -= nread; - bufp = buf; - while (nread) + + while (nread > 0) { - nwrote = write (file, bufp, nread); + int nwrote; + + nwrote = write (file, data, nread); if (nwrote < 0) { pending_error_text = malloc (40); - if (pending_error_text) + if (pending_error_text != NULL) sprintf (pending_error_text, "E unable to write"); pending_error = errno; + + /* Read and discard the file data. */ + while (size > 0) + { + int status, nread; + char *data; + + status = buf_read_data (buf_from_net, size, &data, &nread); + if (status != 0) + return; + size -= nread; + } + return; } nread -= nwrote; - bufp += nwrote; + data += nwrote; } } } @@ -632,7 +951,7 @@ receive_file (size, file, gzipped) int gzip_status; /* Write the file. */ - fd = open (arg, O_WRONLY | O_CREAT | O_TRUNC, 0600); + fd = CVS_OPEN (arg, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { pending_error_text = malloc (40 + strlen (arg)); @@ -689,78 +1008,102 @@ static void serve_modified (arg) char *arg; { - int size; + int size, status; char *size_text; char *mode_text; int gzipped = 0; - if (error_pending ()) return; + /* + * This used to return immediately if error_pending () was true. + * However, that fails, because it causes each line of the file to + * be echoed back to the client as an unrecognized command. The + * client isn't reading from the socket, so eventually both + * processes block trying to write to the other. Now, we try to + * read the file if we can. + */ - mode_text = read_line (stdin); - if (mode_text == NULL) + status = buf_read_line (buf_from_net, &mode_text, (int *) NULL); + if (status != 0) { - pending_error_text = malloc (80 + strlen (arg)); - if (pending_error_text) + if (status == -2) + pending_error = ENOMEM; + else { - if (feof (stdin)) - sprintf (pending_error_text, - "E end of file reading mode for %s", arg); + pending_error_text = malloc (80 + strlen (arg)); + if (pending_error_text == NULL) + pending_error = ENOMEM; else { - sprintf (pending_error_text, - "E error reading mode for %s", arg); - pending_error = errno; + if (status == -1) + sprintf (pending_error_text, + "E end of file reading mode for %s", arg); + else + { + sprintf (pending_error_text, + "E error reading mode for %s", arg); + pending_error = status; + } } } - else - pending_error = ENOMEM; - return; - } - else if (mode_text == NO_MEM_ERROR) - { - pending_error = ENOMEM; return; } - size_text = read_line (stdin); - if (size_text == NULL) + + status = buf_read_line (buf_from_net, &size_text, (int *) NULL); + if (status != 0) { - pending_error_text = malloc (80 + strlen (arg)); - if (pending_error_text) + if (status == -2) + pending_error = ENOMEM; + else { - if (feof (stdin)) - sprintf (pending_error_text, - "E end of file reading size for %s", arg); + pending_error_text = malloc (80 + strlen (arg)); + if (pending_error_text == NULL) + pending_error = ENOMEM; else { - sprintf (pending_error_text, - "E error reading size for %s", arg); - pending_error = errno; + if (status == -1) + sprintf (pending_error_text, + "E end of file reading size for %s", arg); + else + { + sprintf (pending_error_text, + "E error reading size for %s", arg); + pending_error = errno; + } } } - else - pending_error = ENOMEM; - return; - } - else if (size_text == NO_MEM_ERROR) - { - pending_error = ENOMEM; return; } if (size_text[0] == 'z') - { + { gzipped = 1; size = atoi (size_text + 1); - } + } else - size = atoi (size_text); + size = atoi (size_text); free (size_text); + if (error_pending ()) + { + /* Now that we know the size, read and discard the file data. */ + while (size > 0) + { + int status, nread; + char *data; + + status = buf_read_data (buf_from_net, size, &data, &nread); + if (status != 0) + return; + size -= nread; + } + return; + } + if (size >= 0) - { + { receive_file (size, arg, gzipped); if (error_pending ()) return; - } + } { int status = change_mode (arg, mode_text); @@ -777,108 +1120,120 @@ serve_modified (arg) } } -#endif /* SERVER_SUPPORT */ -#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) - -int use_unchanged = 0; - -#endif -#ifdef SERVER_SUPPORT - static void serve_enable_unchanged (arg) char *arg; { - use_unchanged = 1; } +struct an_entry { + struct an_entry *next; + char *entry; +}; + +static struct an_entry *entries; + +static void serve_unchanged PROTO ((char *)); + static void -serve_lost (arg) +serve_unchanged (arg) char *arg; { - if (use_unchanged) - { - /* A missing file already indicates it is nonexistent. */ + struct an_entry *p; + char *name; + char *cp; + char *timefield; + + if (error_pending ()) return; - } - else + + /* Rewrite entries file to have `=' in timestamp field. */ + for (p = entries; p != NULL; p = p->next) { - struct utimbuf ut; - int fd = open (arg, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0 || close (fd) < 0) - { - pending_error = errno; - pending_error_text = malloc (80 + strlen(arg)); - sprintf(pending_error_text, "E cannot open %s", arg); - return; - } - /* - * Set the times to the beginning of the epoch to tell time_stamp() - * that the file was lost. - */ - ut.actime = 0; - ut.modtime = 0; - if (utime (arg, &ut) < 0) + name = p->entry + 1; + cp = strchr (name, '/'); + if (cp != NULL + && strlen (arg) == cp - name + && strncmp (arg, name, cp - name) == 0) { - pending_error = errno; - pending_error_text = malloc (80 + strlen(arg)); - sprintf(pending_error_text, "E cannot utime %s", arg); - return; + timefield = strchr (cp + 1, '/') + 1; + if (*timefield != '=') + { + cp = timefield + strlen (timefield); + cp[1] = '\0'; + while (cp > timefield) + { + *cp = cp[-1]; + --cp; + } + *timefield = '='; + } + break; } } } -struct an_entry { - struct an_entry *next; - char *entry; -}; - -static struct an_entry *entries; +static void serve_is_modified PROTO ((char *)); static void -serve_unchanged (arg) +serve_is_modified (arg) char *arg; { + struct an_entry *p; + char *name; + char *cp; + char *timefield; + /* Have we found this file in "entries" yet. */ + int found; + if (error_pending ()) return; - if (!use_unchanged) - { - /* A missing file already indicates it is unchanged. */ - return; - } - else - { - struct an_entry *p; - char *name; - char *cp; - char *timefield; - /* Rewrite entries file to have `=' in timestamp field. */ - for (p = entries; p != NULL; p = p->next) + /* Rewrite entries file to have `M' in timestamp field. */ + found = 0; + for (p = entries; p != NULL; p = p->next) + { + name = p->entry + 1; + cp = strchr (name, '/'); + if (cp != NULL + && strlen (arg) == cp - name + && strncmp (arg, name, cp - name) == 0) { - name = p->entry + 1; - cp = strchr (name, '/'); - if (cp != NULL - && strlen (arg) == cp - name - && strncmp (arg, name, cp - name) == 0) + timefield = strchr (cp + 1, '/') + 1; + if (!(timefield[0] == 'M' && timefield[1] == '/')) { - timefield = strchr (cp + 1, '/') + 1; - if (*timefield != '=') + cp = timefield + strlen (timefield); + cp[1] = '\0'; + while (cp > timefield) { - cp = timefield + strlen (timefield); - cp[1] = '\0'; - while (cp > timefield) - { - *cp = cp[-1]; - --cp; - } - *timefield = '='; + *cp = cp[-1]; + --cp; } - break; + *timefield = 'M'; } + found = 1; + break; } } + if (!found) + { + /* We got Is-modified but no Entry. Add a dummy entry. + The "D" timestamp is what makes it a dummy. */ + struct an_entry *p; + p = (struct an_entry *) malloc (sizeof (struct an_entry)); + if (p == NULL) + { + pending_error = ENOMEM; + return; + } + p->entry = xmalloc (strlen (arg) + 80); + strcpy (p->entry, "/"); + strcat (p->entry, arg); + strcat (p->entry, "//D//"); + p->next = entries; + entries = p; + } } static void @@ -921,12 +1276,17 @@ server_write_entries () /* Note that we free all the entries regardless of errors. */ if (!error_pending ()) { - f = fopen (CVSADM_ENT, "w"); + /* We open in append mode because we don't want to clobber an + existing Entries file. If we are checking out a module + which explicitly lists more than one file in a particular + directory, then we will wind up calling + server_write_entries for each such file. */ + f = CVS_FOPEN (CVSADM_ENT, "a"); if (f == NULL) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_ENT)); - sprintf(pending_error_text, "E cannot open %s", CVSADM_ENT); + if (alloc_pending (80 + strlen (CVSADM_ENT))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_ENT); } } for (p = entries; p != NULL;) @@ -936,8 +1296,9 @@ server_write_entries () if (fprintf (f, "%s\n", p->entry) < 0) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_ENT)); - sprintf(pending_error_text, "E cannot write to %s", CVSADM_ENT); + if (alloc_pending (80 + strlen(CVSADM_ENT))) + sprintf (pending_error_text, + "E cannot write to %s", CVSADM_ENT); } } free (p->entry); @@ -949,8 +1310,8 @@ server_write_entries () if (f != NULL && fclose (f) == EOF && !error_pending ()) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_ENT)); - sprintf(pending_error_text, "E cannot close %s", CVSADM_ENT); + if (alloc_pending (80 + strlen (CVSADM_ENT))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_ENT); } } @@ -984,6 +1345,7 @@ serve_notify (arg) { struct notify_note *new; char *data; + int status; if (error_pending ()) return; @@ -1010,28 +1372,29 @@ serve_notify (arg) } strcpy (new->filename, arg); - data = read_line (stdin); - if (data == NULL) + status = buf_read_line (buf_from_net, &data, (int *) NULL); + if (status != 0) { - pending_error_text = malloc (80 + strlen (arg)); - if (pending_error_text) + if (status == -2) + pending_error = ENOMEM; + else { - if (feof (stdin)) - sprintf (pending_error_text, - "E end of file reading mode for %s", arg); + pending_error_text = malloc (80 + strlen (arg)); + if (pending_error_text == NULL) + pending_error = ENOMEM; else { - sprintf (pending_error_text, - "E error reading mode for %s", arg); - pending_error = errno; + if (status == -1) + sprintf (pending_error_text, + "E end of file reading notification for %s", arg); + else + { + sprintf (pending_error_text, + "E error reading notification for %s", arg); + pending_error = status; + } } } - else - pending_error = ENOMEM; - } - else if (data == NO_MEM_ERROR) - { - pending_error = ENOMEM; } else { @@ -1092,47 +1455,37 @@ server_notify () { struct notify_note *p; char *repos; - List *list; - Node *node; - int status; while (notify_list != NULL) { - if (chdir (notify_list->dir) < 0) + if ( CVS_CHDIR (notify_list->dir) < 0) { error (0, errno, "cannot change to %s", notify_list->dir); return -1; } repos = Name_Repository (NULL, NULL); - /* Now writelock. */ - list = getlist (); - node = getnode (); - node->type = LOCK; - node->key = xstrdup (repos); - status = addnode (list, node); - assert (status == 0); - Writer_Lock (list); + lock_dir_for_write (repos); fileattr_startdir (repos); notify_do (*notify_list->type, notify_list->filename, getcaller(), notify_list->val, notify_list->watches, repos); - printf ("Notified "); - if (use_dir_and_repos) + buf_output0 (buf_to_net, "Notified "); { char *dir = notify_list->dir + strlen (server_temp_dir) + 1; if (dir[0] == '\0') - fputs (".", stdout); + buf_append_char (buf_to_net, '.'); else - fputs (dir, stdout); - fputs ("/\n", stdout); + buf_output0 (buf_to_net, dir); + buf_append_char (buf_to_net, '/'); + buf_append_char (buf_to_net, '\n'); } - fputs (repos, stdout); - fputs ("/", stdout); - fputs (notify_list->filename, stdout); - fputs ("\n", stdout); + buf_output0 (buf_to_net, repos); + buf_append_char (buf_to_net, '/'); + buf_output0 (buf_to_net, notify_list->filename); + buf_append_char (buf_to_net, '\n'); p = notify_list->next; free (notify_list->filename); @@ -1144,13 +1497,13 @@ server_notify () fileattr_write (); fileattr_free (); - /* Remove the writelock. */ Lock_Cleanup (); - dellist (&list); } - /* do_cvs_command writes to stdout via write(), not stdio, so better - flush out the buffer. */ - fflush (stdout); + + /* The code used to call fflush (stdout) here, but that is no + longer necessary. The data is now buffered in buf_to_net, + which will be flushed by the caller, do_cvs_command. */ + return 0; } @@ -1215,9 +1568,10 @@ serve_global_option (arg) if (arg[0] != '-' || arg[1] == '\0' || arg[2] != '\0') { error_return: - pending_error_text = malloc (strlen (arg) + 80); - sprintf (pending_error_text, "E Protocol error: bad global option %s", - arg); + if (alloc_pending (strlen (arg) + 80)) + sprintf (pending_error_text, + "E Protocol error: bad global option %s", + arg); return; } switch (arg[1]) @@ -1253,62 +1607,27 @@ serve_set (arg) put into pending_error. */ variable_set (arg); } - -/* - * We must read data from a child process and send it across the - * network. We do not want to block on writing to the network, so we - * store the data from the child process in memory. A BUFFER - * structure holds the status of one communication, and uses a linked - * list of buffer_data structures to hold data. - */ - -struct buffer -{ - /* Data. */ - struct buffer_data *data; - - /* Last buffer on data chain. */ - struct buffer_data *last; - /* File descriptor to write to or read from. */ - int fd; - - /* Nonzero if this is an output buffer (sanity check). */ - int output; - - /* Nonzero if the file descriptor is in nonblocking mode. */ - int nonblocking; - - /* Function to call if we can't allocate memory. */ - void (*memory_error) PROTO((struct buffer *)); -}; +#ifdef ENCRYPTION +#ifdef HAVE_KERBEROS -/* Data is stored in lists of these structures. */ - -struct buffer_data +static void +serve_kerberos_encrypt (arg) + char *arg; { - /* Next buffer in linked list. */ - struct buffer_data *next; - - /* - * A pointer into the data area pointed to by the text field. This - * is where to find data that has not yet been written out. - */ - char *bufp; - - /* The number of data bytes found at BUFP. */ - int size; - - /* - * Actual buffer. This never changes after the structure is - * allocated. The buffer is BUFFER_DATA_SIZE bytes. - */ - char *text; -}; - -/* The size we allocate for each buffer_data structure. */ -#define BUFFER_DATA_SIZE (4096) + /* All future communication with the client will be encrypted. */ + + buf_to_net = krb_encrypt_buffer_initialize (buf_to_net, 0, sched, + kblock, + buf_to_net->memory_error); + buf_from_net = krb_encrypt_buffer_initialize (buf_from_net, 1, sched, + kblock, + buf_from_net->memory_error); +} +#endif /* HAVE_KERBEROS */ +#endif /* ENCRYPTION */ + #ifdef SERVER_FLOWCONTROL /* The maximum we'll queue to the remote client before blocking. */ # ifndef SERVER_HI_WATER @@ -1318,274 +1637,9 @@ struct buffer_data # ifndef SERVER_LO_WATER # define SERVER_LO_WATER (1 * 1024 * 1024) # endif /* SERVER_LO_WATER */ -#endif /* SERVER_FLOWCONTROL */ - -/* Linked list of available buffer_data structures. */ -static struct buffer_data *free_buffer_data; - -static void allocate_buffer_datas PROTO((void)); -static inline struct buffer_data *get_buffer_data PROTO((void)); -static int buf_empty_p PROTO((struct buffer *)); -static void buf_output PROTO((struct buffer *, const char *, int)); -static void buf_output0 PROTO((struct buffer *, const char *)); -static inline void buf_append_char PROTO((struct buffer *, int)); -static int buf_send_output PROTO((struct buffer *)); -static int set_nonblock PROTO((struct buffer *)); -static int set_block PROTO((struct buffer *)); -static int buf_send_counted PROTO((struct buffer *)); -static inline void buf_append_data PROTO((struct buffer *, - struct buffer_data *, - struct buffer_data *)); -static int buf_read_file PROTO((FILE *, long, struct buffer_data **, - struct buffer_data **)); -static int buf_input_data PROTO((struct buffer *, int *)); -static void buf_copy_lines PROTO((struct buffer *, struct buffer *, int)); -static int buf_copy_counted PROTO((struct buffer *, struct buffer *)); -#ifdef SERVER_FLOWCONTROL -static int buf_count_mem PROTO((struct buffer *)); static int set_nonblock_fd PROTO((int)); -#endif /* SERVER_FLOWCONTROL */ - -/* Allocate more buffer_data structures. */ - -static void -allocate_buffer_datas () -{ - struct buffer_data *alc; - char *space; - int i; - - /* Allocate buffer_data structures in blocks of 16. */ -#define ALLOC_COUNT (16) - - alc = ((struct buffer_data *) - malloc (ALLOC_COUNT * sizeof (struct buffer_data))); - space = (char *) valloc (ALLOC_COUNT * BUFFER_DATA_SIZE); - if (alc == NULL || space == NULL) - return; - for (i = 0; i < ALLOC_COUNT; i++, alc++, space += BUFFER_DATA_SIZE) - { - alc->next = free_buffer_data; - free_buffer_data = alc; - alc->text = space; - } -} - -/* Get a new buffer_data structure. */ - -static inline struct buffer_data * -get_buffer_data () -{ - struct buffer_data *ret; - - if (free_buffer_data == NULL) - { - allocate_buffer_datas (); - if (free_buffer_data == NULL) - return NULL; - } - - ret = free_buffer_data; - free_buffer_data = ret->next; - return ret; -} - -/* See whether a buffer is empty. */ - -static int -buf_empty_p (buf) - struct buffer *buf; -{ - struct buffer_data *data; - - for (data = buf->data; data != NULL; data = data->next) - if (data->size > 0) - return 0; - return 1; -} - -#ifdef SERVER_FLOWCONTROL -/* - * Count how much data is stored in the buffer.. - * Note that each buffer is a malloc'ed chunk BUFFER_DATA_SIZE. - */ - -static int -buf_count_mem (buf) - struct buffer *buf; -{ - struct buffer_data *data; - int mem = 0; - - for (data = buf->data; data != NULL; data = data->next) - mem += BUFFER_DATA_SIZE; - - return mem; -} -#endif /* SERVER_FLOWCONTROL */ - -/* Add data DATA of length LEN to BUF. */ - -static void -buf_output (buf, data, len) - struct buffer *buf; - const char *data; - int len; -{ - if (buf->data != NULL - && (((buf->last->text + BUFFER_DATA_SIZE) - - (buf->last->bufp + buf->last->size)) - >= len)) - { - memcpy (buf->last->bufp + buf->last->size, data, len); - buf->last->size += len; - return; - } - - while (1) - { - struct buffer_data *newdata; - - newdata = get_buffer_data (); - if (newdata == NULL) - { - (*buf->memory_error) (buf); - return; - } - - if (buf->data == NULL) - buf->data = newdata; - else - buf->last->next = newdata; - newdata->next = NULL; - buf->last = newdata; - - newdata->bufp = newdata->text; - - if (len <= BUFFER_DATA_SIZE) - { - newdata->size = len; - memcpy (newdata->text, data, len); - return; - } - - newdata->size = BUFFER_DATA_SIZE; - memcpy (newdata->text, data, BUFFER_DATA_SIZE); - - data += BUFFER_DATA_SIZE; - len -= BUFFER_DATA_SIZE; - } - - /*NOTREACHED*/ -} - -/* Add a '\0' terminated string to BUF. */ - -static void -buf_output0 (buf, string) - struct buffer *buf; - const char *string; -{ - buf_output (buf, string, strlen (string)); -} - -/* Add a single character to BUF. */ - -static inline void -buf_append_char (buf, ch) - struct buffer *buf; - int ch; -{ - if (buf->data != NULL - && (buf->last->text + BUFFER_DATA_SIZE - != buf->last->bufp + buf->last->size)) - { - *(buf->last->bufp + buf->last->size) = ch; - ++buf->last->size; - } - else - { - char b; - - b = ch; - buf_output (buf, &b, 1); - } -} - -/* - * Send all the output we've been saving up. Returns 0 for success or - * errno code. If the buffer has been set to be nonblocking, this - * will just write until the write would block. - */ - -static int -buf_send_output (buf) - struct buffer *buf; -{ - if (! buf->output) - abort (); - - while (buf->data != NULL) - { - struct buffer_data *data; - - data = buf->data; - while (data->size > 0) - { - int nbytes; - - nbytes = write (buf->fd, data->bufp, data->size); - if (nbytes <= 0) - { - int status; - - if (buf->nonblocking - && (nbytes == 0 -#ifdef EWOULDBLOCK - || errno == EWOULDBLOCK -#endif - || errno == EAGAIN)) - { - /* - * A nonblocking write failed to write any data. - * Just return. - */ - return 0; - } - - /* - * An error, or EOF. Throw away all the data and - * return. - */ - if (nbytes == 0) - status = EIO; - else - status = errno; - - buf->last->next = free_buffer_data; - free_buffer_data = buf->data; - buf->data = NULL; - buf->last = NULL; - - return status; - } - - data->size -= nbytes; - data->bufp += nbytes; - } - - buf->data = data->next; - data->next = free_buffer_data; - free_buffer_data = data; - } - - buf->last = NULL; - return 0; -} - -#ifdef SERVER_FLOWCONTROL /* * Set buffer BUF to non-blocking I/O. Returns 0 for success or errno * code. @@ -1604,553 +1658,9 @@ set_nonblock_fd (fd) return errno; return 0; } -#endif /* SERVER_FLOWCONTROL */ - -static int -set_nonblock (buf) - struct buffer *buf; -{ - int flags; - - if (buf->nonblocking) - return 0; - flags = fcntl (buf->fd, F_GETFL, 0); - if (flags < 0) - return errno; - if (fcntl (buf->fd, F_SETFL, flags | O_NONBLOCK) < 0) - return errno; - buf->nonblocking = 1; - return 0; -} - -/* - * Set buffer BUF to blocking I/O. Returns 0 for success or errno - * code. - */ - -static int -set_block (buf) - struct buffer *buf; -{ - int flags; - - if (! buf->nonblocking) - return 0; - flags = fcntl (buf->fd, F_GETFL, 0); - if (flags < 0) - return errno; - if (fcntl (buf->fd, F_SETFL, flags & ~O_NONBLOCK) < 0) - return errno; - buf->nonblocking = 0; - return 0; -} - -/* - * Send a character count and some output. Returns errno code or 0 for - * success. - * - * Sending the count in binary is OK since this is only used on a pipe - * within the same system. - */ - -static int -buf_send_counted (buf) - struct buffer *buf; -{ - int size; - struct buffer_data *data; - - if (! buf->output) - abort (); - - size = 0; - for (data = buf->data; data != NULL; data = data->next) - size += data->size; - - data = get_buffer_data (); - if (data == NULL) - { - (*buf->memory_error) (buf); - return ENOMEM; - } - - data->next = buf->data; - buf->data = data; - if (buf->last == NULL) - buf->last = data; - - data->bufp = data->text; - data->size = sizeof (int); - - *((int *) data->text) = size; - - return buf_send_output (buf); -} - -/* Append a list of buffer_data structures to an buffer. */ - -static inline void -buf_append_data (buf, data, last) - struct buffer *buf; - struct buffer_data *data; - struct buffer_data *last; -{ - if (data != NULL) - { - if (buf->data == NULL) - buf->data = data; - else - buf->last->next = data; - buf->last = last; - } -} - -/* - * Copy the contents of file F into buffer_data structures. We can't - * copy directly into an buffer, because we want to handle failure and - * succeess differently. Returns 0 on success, or -2 if out of - * memory, or a status code on error. Since the caller happens to - * know the size of the file, it is passed in as SIZE. On success, - * this function sets *RETP and *LASTP, which may be passed to - * buf_append_data. - */ - -static int -buf_read_file (f, size, retp, lastp) - FILE *f; - long size; - struct buffer_data **retp; - struct buffer_data **lastp; -{ - int status; - - *retp = NULL; - *lastp = NULL; - - while (size > 0) - { - struct buffer_data *data; - int get; - - data = get_buffer_data (); - if (data == NULL) - { - status = -2; - goto error_return; - } - - if (*retp == NULL) - *retp = data; - else - (*lastp)->next = data; - data->next = NULL; - *lastp = data; - - data->bufp = data->text; - data->size = 0; - - if (size > BUFFER_DATA_SIZE) - get = BUFFER_DATA_SIZE; - else - get = size; - - errno = EIO; - if (fread (data->text, get, 1, f) != 1) - { - status = errno; - goto error_return; - } - - data->size += get; - size -= get; - } - - return 0; - - error_return: - if (*retp != NULL) - { - (*lastp)->next = free_buffer_data; - free_buffer_data = *retp; - } - return status; -} - -static int -buf_read_file_to_eof (f, retp, lastp) - FILE *f; - struct buffer_data **retp; - struct buffer_data **lastp; -{ - int status; - - *retp = NULL; - *lastp = NULL; - - while (!feof (f)) - { - struct buffer_data *data; - int get, nread; - - data = get_buffer_data (); - if (data == NULL) - { - status = -2; - goto error_return; - } - - if (*retp == NULL) - *retp = data; - else - (*lastp)->next = data; - data->next = NULL; - *lastp = data; - - data->bufp = data->text; - data->size = 0; - - get = BUFFER_DATA_SIZE; - - errno = EIO; - nread = fread (data->text, 1, get, f); - if (nread == 0 && !feof (f)) - { - status = errno; - goto error_return; - } - - data->size = nread; - } - - return 0; - - error_return: - if (*retp != NULL) - { - (*lastp)->next = free_buffer_data; - free_buffer_data = *retp; - } - return status; -} - -static int -buf_chain_length (buf) - struct buffer_data *buf; -{ - int size = 0; - while (buf) - { - size += buf->size; - buf = buf->next; - } - return size; -} - -/* - * Read an arbitrary amount of data from a file descriptor into an - * input buffer. The file descriptor will be in nonblocking mode, and - * we just grab what we can. Return 0 on success, or -1 on end of - * file, or -2 if out of memory, or an error code. If COUNTP is not - * NULL, *COUNTP is set to the number of bytes read. - */ - -static int -buf_input_data (buf, countp) - struct buffer *buf; - int *countp; -{ - if (buf->output) - abort (); - - if (countp != NULL) - *countp = 0; - - while (1) - { - int get; - int nbytes; - - if (buf->data == NULL - || (buf->last->bufp + buf->last->size - == buf->last->text + BUFFER_DATA_SIZE)) - { - struct buffer_data *data; - - data = get_buffer_data (); - if (data == NULL) - { - (*buf->memory_error) (buf); - return -2; - } - - if (buf->data == NULL) - buf->data = data; - else - buf->last->next = data; - data->next = NULL; - buf->last = data; - - data->bufp = data->text; - data->size = 0; - } - - get = ((buf->last->text + BUFFER_DATA_SIZE) - - (buf->last->bufp + buf->last->size)); - nbytes = read (buf->fd, buf->last->bufp + buf->last->size, get); - if (nbytes <= 0) - { - if (nbytes == 0) - { - /* - * This assumes that we are using POSIX or BSD style - * nonblocking I/O. On System V we will get a zero - * return if there is no data, even when not at EOF. - */ - return -1; - } - - if (errno == EAGAIN -#ifdef EWOULDBLOCK - || errno == EWOULDBLOCK -#endif - ) - return 0; - - return errno; - } - - buf->last->size += nbytes; - if (countp != NULL) - *countp += nbytes; - } - - /*NOTREACHED*/ -} - -/* - * Copy lines from an input buffer to an output buffer. This copies - * all complete lines (characters up to a newline) from INBUF to - * OUTBUF. Each line in OUTBUF is preceded by the character COMMAND - * and a space. - */ - -static void -buf_copy_lines (outbuf, inbuf, command) - struct buffer *outbuf; - struct buffer *inbuf; - int command; -{ - if (! outbuf->output || inbuf->output) - abort (); - - while (1) - { - struct buffer_data *data; - struct buffer_data *nldata; - char *nl; - int len; - - /* See if there is a newline in INBUF. */ - nldata = NULL; - nl = NULL; - for (data = inbuf->data; data != NULL; data = data->next) - { - nl = memchr (data->bufp, '\n', data->size); - if (nl != NULL) - { - nldata = data; - break; - } - } - - if (nldata == NULL) - { - /* There are no more lines in INBUF. */ - return; - } - - /* Put in the command. */ - buf_append_char (outbuf, command); - buf_append_char (outbuf, ' '); - - if (inbuf->data != nldata) - { - /* - * Simply move over all the buffers up to the one containing - * the newline. - */ - for (data = inbuf->data; data->next != nldata; data = data->next) - ; - data->next = NULL; - buf_append_data (outbuf, inbuf->data, data); - inbuf->data = nldata; - } - - /* - * If the newline is at the very end of the buffer, just move - * the buffer onto OUTBUF. Otherwise we must copy the data. - */ - len = nl + 1 - nldata->bufp; - if (len == nldata->size) - { - inbuf->data = nldata->next; - if (inbuf->data == NULL) - inbuf->last = NULL; - - nldata->next = NULL; - buf_append_data (outbuf, nldata, nldata); - } - else - { - buf_output (outbuf, nldata->bufp, len); - nldata->bufp += len; - nldata->size -= len; - } - } -} - -/* - * Copy counted data from one buffer to another. The count is an - * integer, host size, host byte order (it is only used across a - * pipe). If there is enough data, it should be moved over. If there - * is not enough data, it should remain on the original buffer. This - * returns the number of bytes it needs to see in order to actually - * copy something over. - */ - -static int -buf_copy_counted (outbuf, inbuf) - struct buffer *outbuf; - struct buffer *inbuf; -{ - if (! outbuf->output || inbuf->output) - abort (); - - while (1) - { - struct buffer_data *data; - int need; - union - { - char intbuf[sizeof (int)]; - int i; - } u; - char *intp; - int count; - struct buffer_data *start; - int startoff; - struct buffer_data *stop; - int stopwant; - - /* See if we have enough bytes to figure out the count. */ - need = sizeof (int); - intp = u.intbuf; - for (data = inbuf->data; data != NULL; data = data->next) - { - if (data->size >= need) - { - memcpy (intp, data->bufp, need); - break; - } - memcpy (intp, data->bufp, data->size); - intp += data->size; - need -= data->size; - } - if (data == NULL) - { - /* We don't have enough bytes to form an integer. */ - return need; - } - - count = u.i; - start = data; - startoff = need; - - /* - * We have an integer in COUNT. We have gotten all the data - * from INBUF in all buffers before START, and we have gotten - * STARTOFF bytes from START. See if we have enough bytes - * remaining in INBUF. - */ - need = count - (start->size - startoff); - if (need <= 0) - { - stop = start; - stopwant = count; - } - else - { - for (data = start->next; data != NULL; data = data->next) - { - if (need <= data->size) - break; - need -= data->size; - } - if (data == NULL) - { - /* We don't have enough bytes. */ - return need; - } - stop = data; - stopwant = need; - } - - /* - * We have enough bytes. Free any buffers in INBUF before - * START, and remove STARTOFF bytes from START, so that we can - * forget about STARTOFF. - */ - start->bufp += startoff; - start->size -= startoff; - - if (start->size == 0) - start = start->next; - - if (stop->size == stopwant) - { - stop = stop->next; - stopwant = 0; - } - - while (inbuf->data != start) - { - data = inbuf->data; - inbuf->data = data->next; - data->next = free_buffer_data; - free_buffer_data = data; - } - /* - * We want to copy over the bytes from START through STOP. We - * only want STOPWANT bytes from STOP. - */ - - if (start != stop) - { - /* Attach the buffers from START through STOP to OUTBUF. */ - for (data = start; data->next != stop; data = data->next) - ; - inbuf->data = stop; - data->next = NULL; - buf_append_data (outbuf, start, data); - } - - if (stopwant > 0) - { - buf_output (outbuf, stop->bufp, stopwant); - stop->bufp += stopwant; - stop->size -= stopwant; - } - } - - /*NOTREACHED*/ -} +#endif /* SERVER_FLOWCONTROL */ -/* While processing requests, this buffer accumulates data to be sent to - the client, and then once we are in do_cvs_command, we use it - for all the data to be sent. */ -static struct buffer buf_to_net; - static void serve_questionable PROTO((char *)); static void @@ -2169,7 +1679,7 @@ serve_questionable (arg) if (dir_name == NULL) { - buf_output0 (&buf_to_net, "E Protocol error: 'Directory' missing"); + buf_output0 (buf_to_net, "E Protocol error: 'Directory' missing"); return; } @@ -2177,15 +1687,15 @@ serve_questionable (arg) { char *update_dir; - buf_output (&buf_to_net, "M ? ", 4); + buf_output (buf_to_net, "M ? ", 4); update_dir = dir_name + strlen (server_temp_dir) + 1; if (!(update_dir[0] == '.' && update_dir[1] == '\0')) { - buf_output0 (&buf_to_net, update_dir); - buf_output (&buf_to_net, "/", 1); + buf_output0 (buf_to_net, update_dir); + buf_output (buf_to_net, "/", 1); } - buf_output0 (&buf_to_net, arg); - buf_output (&buf_to_net, "\n", 1); + buf_output0 (buf_to_net, arg); + buf_output (buf_to_net, "\n", 1); } } @@ -2198,14 +1708,14 @@ serve_case (arg) ign_case = 1; } -static struct buffer protocol; +static struct buffer *protocol; /* This is the output which we are saving up to send to the server, in the child process. We will push it through, via the `protocol' buffer, when we have a complete line. */ -static struct buffer saved_output; +static struct buffer *saved_output; /* Likewise, but stuff which will go to stderr. */ -static struct buffer saved_outerr; +static struct buffer *saved_outerr; static void protocol_memory_error (buf) @@ -2237,8 +1747,7 @@ error ENOMEM Virtual memory exhausted.\n"; /* If this gives an error, not much we could do. syslog() it? */ write (STDOUT_FILENO, msg, sizeof (msg) - 1); - server_cleanup (0); - exit (EXIT_FAILURE); + error_exit (); } static void @@ -2248,6 +1757,155 @@ input_memory_error (buf) outbuf_memory_error (buf); } + + +/* If command is legal, return 1. + * Else if command is illegal and croak_on_illegal is set, then die. + * Else just return 0 to indicate that command is illegal. + */ +static int +check_command_legal_p (cmd_name) + char *cmd_name; +{ + /* Right now, only pserver notices illegal commands -- namely, + * write attempts by a read-only user. Therefore, if CVS_Username + * is not set, this just returns 1, because CVS_Username unset + * means pserver is not active. + */ +#ifdef AUTH_SERVER_SUPPORT + if (CVS_Username == NULL) + return 1; + + if (lookup_command_attribute (cmd_name) & CVS_CMD_MODIFIES_REPOSITORY) + { + /* This command has the potential to modify the repository, so + * we check if the user have permission to do that. + * + * (Only relevant for remote users -- local users can do + * whatever normal Unix file permissions allow them to do.) + * + * The decision method: + * + * If $CVSROOT/CVSADMROOT_READERS exists and user is listed + * in it, then read-only access for user. + * + * Or if $CVSROOT/CVSADMROOT_WRITERS exists and user NOT + * listed in it, then also read-only access for user. + * + * Else read-write access for user. + */ + + char *linebuf = NULL; + int num_red = 0; + size_t linebuf_len = 0; + char *fname; + size_t flen; + FILE *fp; + int found_it = 0; + + /* else */ + flen = strlen (CVSroot_directory) + + strlen (CVSROOTADM) + + strlen (CVSROOTADM_READERS) + + 3; + + fname = xmalloc (flen); + (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_READERS); + + fp = fopen (fname, "r"); + free (fname); + + if (fp == NULL) + goto do_writers; + else /* successfully opened readers file */ + { + while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + /* Hmmm, is it worth importing my own readline + library into CVS? It takes care of chopping + leading and trailing whitespace, "#" comments, and + newlines automatically when so requested. Would + save some code here... -kff */ + + /* Chop newline by hand, for strcmp()'s sake. */ + if (linebuf[num_red - 1] == '\n') + linebuf[num_red - 1] = '\0'; + + if (strcmp (linebuf, CVS_Username) == 0) + goto handle_illegal; + } + + /* If not listed specifically as a reader, then this user + has write access by default unless writers are also + specified in a file . */ + fclose (fp); + goto do_writers; + } + + do_writers: + + flen = strlen (CVSroot_directory) + + strlen (CVSROOTADM) + + strlen (CVSROOTADM_WRITERS) + + 3; + + fname = xmalloc (flen); + (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_WRITERS); + + fp = fopen (fname, "r"); + free (fname); + + if (fp == NULL) + { + /* writers file does not exist, so everyone is a writer, + by default */ + if (linebuf) + free (linebuf); + return 1; + } + + /* else */ + + found_it = 0; + while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + /* Chop newline by hand, for strcmp()'s sake. */ + if (linebuf[num_red - 1] == '\n') + linebuf[num_red - 1] = '\0'; + + if (strcmp (linebuf, CVS_Username) == 0) + { + found_it = 1; + break; + } + } + + if (found_it) + { + fclose (fp); + if (linebuf) + free (linebuf); + return 1; + } + else /* writers file exists, but this user not listed in it */ + { + handle_illegal: + fclose (fp); + if (linebuf) + free (linebuf); + return 0; + } + } +#endif /* AUTH_SERVER_SUPPORT */ + + /* If ever reach end of this function, command must be legal. */ + return 1; +} + + + /* Execute COMMAND in a subprocess with the approriate funky things done. */ static struct fd_set_wrapper { fd_set fds; } command_fds_to_drain; @@ -2258,7 +1916,8 @@ static int flowcontrol_pipe[2]; #endif /* SERVER_FLOWCONTROL */ static void -do_cvs_command (command) +do_cvs_command (cmd_name, command) + char *cmd_name; int (*command) PROTO((int argc, char **argv)); { /* @@ -2294,6 +1953,21 @@ do_cvs_command (command) if (print_pending_error ()) goto free_args_and_return; + /* Global `command_name' is probably "server" right now -- only + serve_export() sets it to anything else. So we will use local + parameter `cmd_name' to determine if this command is legal for + this user. */ + if (!check_command_legal_p (cmd_name)) + { + buf_output0 (buf_to_net, "E "); + buf_output0 (buf_to_net, program_name); + buf_output0 (buf_to_net, " [server aborted]: \""); + buf_output0 (buf_to_net, cmd_name); + buf_output0 (buf_to_net, "\" requires write access to the repository\n\ +error \n"); + goto free_args_and_return; + } + (void) server_notify (); /* @@ -2329,13 +2003,29 @@ do_cvs_command (command) set_nonblock_fd (flowcontrol_pipe[1]); #endif /* SERVER_FLOWCONTROL */ - dev_null_fd = open ("/dev/null", O_RDONLY); + dev_null_fd = CVS_OPEN (DEVNULL, O_RDONLY); if (dev_null_fd < 0) { print_error (errno); goto error_exit; } + /* We shouldn't have any partial lines from cvs_output and + cvs_outerr, but we handle them here in case there is a bug. */ + if (! buf_empty_p (saved_output)) + { + buf_append_char (saved_output, '\n'); + buf_copy_lines (buf_to_net, saved_output, 'M'); + } + if (! buf_empty_p (saved_outerr)) + { + buf_append_char (saved_outerr, '\n'); + buf_copy_lines (buf_to_net, saved_outerr, 'E'); + } + + /* Flush out any pending data. */ + buf_flush (buf_to_net, 1); + /* Don't use vfork; we're not going to exec(). */ command_pid = fork (); if (command_pid < 0) @@ -2352,18 +2042,20 @@ do_cvs_command (command) flag. */ error_use_protocol = 0; - protocol.data = protocol.last = NULL; - protocol.fd = protocol_pipe[1]; - protocol.output = 1; - protocol.nonblocking = 0; - protocol.memory_error = protocol_memory_error; + protocol = fd_buffer_initialize (protocol_pipe[1], 0, + protocol_memory_error); + + /* At this point we should no longer be using buf_to_net and + buf_from_net. Instead, everything should go through + protocol. */ + buf_to_net = NULL; + buf_from_net = NULL; - saved_output.data = saved_output.last = NULL; - saved_output.fd = -1; - saved_output.output = 0; - saved_output.nonblocking = 0; - saved_output.memory_error = protocol_memory_error; - saved_outerr = saved_output; + /* These were originally set up to use outbuf_memory_error. + Since we're now in the child, we should use the simpler + protocol_memory_error function. */ + saved_output->memory_error = protocol_memory_error; + saved_outerr->memory_error = protocol_memory_error; if (dup2 (dev_null_fd, STDIN_FILENO) < 0) error (1, errno, "can't set up pipes"); @@ -2399,9 +2091,9 @@ do_cvs_command (command) /* OK, sit around getting all the input from the child. */ { - struct buffer stdoutbuf; - struct buffer stderrbuf; - struct buffer protocol_inbuf; + struct buffer *stdoutbuf; + struct buffer *stderrbuf; + struct buffer *protocol_inbuf; /* Number of file descriptors to check in select (). */ int num_to_check; int count_needed = 0; @@ -2428,32 +2120,25 @@ do_cvs_command (command) ++num_to_check; if (num_to_check > FD_SETSIZE) { - printf ("E internal error: FD_SETSIZE not big enough.\nerror \n"); + buf_output0 (buf_to_net, + "E internal error: FD_SETSIZE not big enough.\n\ +error \n"); goto error_exit; } - stdoutbuf.data = stdoutbuf.last = NULL; - stdoutbuf.fd = stdout_pipe[0]; - stdoutbuf.output = 0; - stdoutbuf.nonblocking = 0; - stdoutbuf.memory_error = input_memory_error; - - stderrbuf.data = stderrbuf.last = NULL; - stderrbuf.fd = stderr_pipe[0]; - stderrbuf.output = 0; - stderrbuf.nonblocking = 0; - stderrbuf.memory_error = input_memory_error; - - protocol_inbuf.data = protocol_inbuf.last = NULL; - protocol_inbuf.fd = protocol_pipe[0]; - protocol_inbuf.output = 0; - protocol_inbuf.nonblocking = 0; - protocol_inbuf.memory_error = input_memory_error; - - set_nonblock (&buf_to_net); - set_nonblock (&stdoutbuf); - set_nonblock (&stderrbuf); - set_nonblock (&protocol_inbuf); + stdoutbuf = fd_buffer_initialize (stdout_pipe[0], 1, + input_memory_error); + + stderrbuf = fd_buffer_initialize (stderr_pipe[0], 1, + input_memory_error); + + protocol_inbuf = fd_buffer_initialize (protocol_pipe[0], 1, + input_memory_error); + + set_nonblock (buf_to_net); + set_nonblock (stdoutbuf); + set_nonblock (stderrbuf); + set_nonblock (protocol_inbuf); if (close (stdout_pipe[1]) < 0) { @@ -2506,7 +2191,7 @@ do_cvs_command (command) * See if we are swamping the remote client and filling our VM. * Tell child to hold off if we do. */ - bufmemsize = buf_count_mem (&buf_to_net); + bufmemsize = buf_count_mem (buf_to_net); if (!have_flowcontrolled && (bufmemsize > SERVER_HI_WATER)) { if (write(flowcontrol_pipe[1], "S", 1) == 1) @@ -2521,7 +2206,7 @@ do_cvs_command (command) FD_ZERO (&readfds); FD_ZERO (&writefds); - if (! buf_empty_p (&buf_to_net)) + if (! buf_empty_p (buf_to_net)) FD_SET (STDOUT_FILENO, &writefds); if (stdout_pipe[0] >= 0) @@ -2537,6 +2222,14 @@ do_cvs_command (command) FD_SET (protocol_pipe[0], &readfds); } + /* This process of selecting on the three pipes means that + we might not get output in the same order in which it + was written, thus producing the well-known + "out-of-order" bug. If the child process uses + cvs_output and cvs_outerr, it will send everything on + the protocol_pipe and avoid this problem, so the + solution is to use cvs_output and cvs_outerr in the + child process. */ do { /* This used to select on exceptions too, but as far as I know there was never any reason to do that and @@ -2554,7 +2247,7 @@ do_cvs_command (command) if (FD_ISSET (STDOUT_FILENO, &writefds)) { /* What should we do with errors? syslog() them? */ - buf_send_output (&buf_to_net); + buf_send_output (buf_to_net); } if (stdout_pipe[0] >= 0 @@ -2562,9 +2255,9 @@ do_cvs_command (command) { int status; - status = buf_input_data (&stdoutbuf, (int *) NULL); + status = buf_input_data (stdoutbuf, (int *) NULL); - buf_copy_lines (&buf_to_net, &stdoutbuf, 'M'); + buf_copy_lines (buf_to_net, stdoutbuf, 'M'); if (status == -1) stdout_pipe[0] = -1; @@ -2575,7 +2268,7 @@ do_cvs_command (command) } /* What should we do with errors? syslog() them? */ - buf_send_output (&buf_to_net); + buf_send_output (buf_to_net); } if (stderr_pipe[0] >= 0 @@ -2583,9 +2276,9 @@ do_cvs_command (command) { int status; - status = buf_input_data (&stderrbuf, (int *) NULL); + status = buf_input_data (stderrbuf, (int *) NULL); - buf_copy_lines (&buf_to_net, &stderrbuf, 'E'); + buf_copy_lines (buf_to_net, stderrbuf, 'E'); if (status == -1) stderr_pipe[0] = -1; @@ -2596,7 +2289,7 @@ do_cvs_command (command) } /* What should we do with errors? syslog() them? */ - buf_send_output (&buf_to_net); + buf_send_output (buf_to_net); } if (protocol_pipe[0] >= 0 @@ -2604,8 +2297,17 @@ do_cvs_command (command) { int status; int count_read; + int special; - status = buf_input_data (&protocol_inbuf, &count_read); + status = buf_input_data (protocol_inbuf, &count_read); + + if (status == -1) + protocol_pipe[0] = -1; + else if (status > 0) + { + print_error (status); + goto error_exit; + } /* * We only call buf_copy_counted if we have read @@ -2614,20 +2316,30 @@ do_cvs_command (command) * have. */ count_needed -= count_read; - if (count_needed <= 0) - count_needed = buf_copy_counted (&buf_to_net, - &protocol_inbuf); - - if (status == -1) - protocol_pipe[0] = -1; - else if (status > 0) + while (count_needed <= 0) { - print_error (status); - goto error_exit; - } + count_needed = buf_copy_counted (buf_to_net, + protocol_inbuf, + &special); + + /* What should we do with errors? syslog() them? */ + buf_send_output (buf_to_net); + + /* If SPECIAL got set to -1, it means that the child + wants us to flush the pipe. We don't want to block + on the network, but we flush what we can. If the + client supports the 'F' command, we send it. */ + if (special == -1) + { + if (supported_response ("F")) + { + buf_append_char (buf_to_net, 'F'); + buf_append_char (buf_to_net, '\n'); + } - /* What should we do with errors? syslog() them? */ - buf_send_output (&buf_to_net); + cvs_flusherr (); + } + } } } @@ -2636,18 +2348,18 @@ do_cvs_command (command) * anything left on stdoutbuf or stderrbuf (this could only * happen if there was no trailing newline), send it over. */ - if (! buf_empty_p (&stdoutbuf)) + if (! buf_empty_p (stdoutbuf)) { - buf_append_char (&stdoutbuf, '\n'); - buf_copy_lines (&buf_to_net, &stdoutbuf, 'M'); + buf_append_char (stdoutbuf, '\n'); + buf_copy_lines (buf_to_net, stdoutbuf, 'M'); } - if (! buf_empty_p (&stderrbuf)) + if (! buf_empty_p (stderrbuf)) { - buf_append_char (&stderrbuf, '\n'); - buf_copy_lines (&buf_to_net, &stderrbuf, 'E'); + buf_append_char (stderrbuf, '\n'); + buf_copy_lines (buf_to_net, stderrbuf, 'E'); } - if (! buf_empty_p (&protocol_inbuf)) - buf_output0 (&buf_to_net, + if (! buf_empty_p (protocol_inbuf)) + buf_output0 (buf_to_net, "E Protocol error: uncounted data discarded\n"); errs = 0; @@ -2671,6 +2383,7 @@ do_cvs_command (command) else { int sig = WTERMSIG (status); + char buf[50]; /* * This is really evil, because signals might be numbered * differently on the two systems. We should be using @@ -2678,14 +2391,17 @@ do_cvs_command (command) * variety). But cvs doesn't currently use libiberty...we * could roll our own.... FIXME. */ - printf ("E Terminated with fatal signal %d\n", sig); + buf_output0 (buf_to_net, "E Terminated with fatal signal "); + sprintf (buf, "%d\n", sig); + buf_output0 (buf_to_net, buf); /* Test for a core dump. Is this portable? */ if (status & 0x80) { - printf ("E Core dumped; preserving %s on server.\n\ -E CVS locks may need cleaning up.\n", - server_temp_dir); + buf_output0 (buf_to_net, "E Core dumped; preserving "); + buf_output0 (buf_to_net, orig_server_temp_dir); + buf_output0 (buf_to_net, " on server.\n\ +E CVS locks may need cleaning up.\n"); dont_delete_temp = 1; } ++errs; @@ -2698,15 +2414,15 @@ E CVS locks may need cleaning up.\n", * OK, we've waited for the child. By now all CVS locks are free * and it's OK to block on the network. */ - set_block (&buf_to_net); - buf_send_output (&buf_to_net); + set_block (buf_to_net); + buf_flush (buf_to_net, 1); } if (errs) /* We will have printed an error message already. */ - printf ("error \n"); + buf_output0 (buf_to_net, "error \n"); else - printf ("ok\n"); + buf_output0 (buf_to_net, "ok\n"); goto free_args_and_return; error_exit: @@ -2743,6 +2459,11 @@ E CVS locks may need cleaning up.\n", argument_count = 1; } + + /* Flush out any data not yet sent. */ + set_block (buf_to_net); + buf_flush (buf_to_net, 1); + return; } @@ -2787,7 +2508,9 @@ server_pause_check() if (FD_ISSET (flowcontrol_pipe[0], &fds)) { - while (read (flowcontrol_pipe[0], buf, 1) == 1) + int got; + + while ((got = read (flowcontrol_pipe[0], buf, 1)) == 1) { if (*buf == 'S') /* Stop */ paused = 1; @@ -2796,11 +2519,24 @@ server_pause_check() else return; /* ??? */ } + + /* This assumes that we are using BSD or POSIX nonblocking + I/O. System V nonblocking I/O returns zero if there is + nothing to read. */ + if (got == 0) + error (1, 0, "flow control EOF"); + if (got < 0 && ! blocking_error (errno)) + { + error (1, errno, "flow control read failed"); + } } } } #endif /* SERVER_FLOWCONTROL */ +/* This variable commented in server.h. */ +char *server_dir = NULL; + static void output_dir PROTO((char *, char *)); static void @@ -2808,16 +2544,18 @@ output_dir (update_dir, repository) char *update_dir; char *repository; { - if (use_dir_and_repos) + if (server_dir != NULL) { - if (update_dir[0] == '\0') - buf_output0 (&protocol, "."); - else - buf_output0 (&protocol, update_dir); - buf_output0 (&protocol, "/\n"); + buf_output0 (protocol, server_dir); + buf_output0 (protocol, "/"); } - buf_output0 (&protocol, repository); - buf_output0 (&protocol, "/"); + if (update_dir[0] == '\0') + buf_output0 (protocol, "."); + else + buf_output0 (protocol, update_dir); + buf_output0 (protocol, "/\n"); + buf_output0 (protocol, repository); + buf_output0 (protocol, "/"); } /* @@ -2929,9 +2667,9 @@ server_scratch (fname) if (scratched_file != NULL) { - buf_output0 (&protocol, + buf_output0 (protocol, "E CVS server internal error: duplicate Scratch_Entry\n"); - buf_send_counted (&protocol); + buf_send_counted (protocol); return; } scratched_file = xstrdup (fname); @@ -2950,22 +2688,23 @@ new_entries_line () { if (entries_line) { - buf_output0 (&protocol, entries_line); - buf_output (&protocol, "\n", 1); + buf_output0 (protocol, entries_line); + buf_output (protocol, "\n", 1); } else /* Return the error message as the Entries line. */ - buf_output0 (&protocol, + buf_output0 (protocol, "CVS server internal error: Register missing\n"); free (entries_line); entries_line = NULL; } + static void serve_ci (arg) char *arg; { - do_cvs_command (commit); + do_cvs_command ("commit", commit); } static void @@ -2979,7 +2718,7 @@ checked_in_response (file, update_dir, repository) struct stat sb; char *mode_string; - if (stat (file, &sb) < 0) + if ( CVS_STAT (file, &sb) < 0) { /* Not clear to me why the file would fail to exist, but it was happening somewhere in the testsuite. */ @@ -2988,18 +2727,18 @@ checked_in_response (file, update_dir, repository) } else { - buf_output0 (&protocol, "Mode "); + buf_output0 (protocol, "Mode "); mode_string = mode_to_string (sb.st_mode); - buf_output0 (&protocol, mode_string); - buf_output0 (&protocol, "\n"); + buf_output0 (protocol, mode_string); + buf_output0 (protocol, "\n"); free (mode_string); } } - buf_output0 (&protocol, "Checked-in "); + buf_output0 (protocol, "Checked-in "); output_dir (update_dir, repository); - buf_output0 (&protocol, file); - buf_output (&protocol, "\n", 1); + buf_output0 (protocol, file); + buf_output (protocol, "\n", 1); new_entries_line (); } @@ -3017,10 +2756,10 @@ server_checked_in (file, update_dir, repository) * This happens if we are now doing a "cvs remove" after a previous * "cvs add" (without a "cvs ci" in between). */ - buf_output0 (&protocol, "Remove-entry "); + buf_output0 (protocol, "Remove-entry "); output_dir (update_dir, repository); - buf_output0 (&protocol, file); - buf_output (&protocol, "\n", 1); + buf_output0 (protocol, file); + buf_output (protocol, "\n", 1); free (scratched_file); scratched_file = NULL; } @@ -3028,7 +2767,7 @@ server_checked_in (file, update_dir, repository) { checked_in_response (file, update_dir, repository); } - buf_send_counted (&protocol); + buf_send_counted (protocol); } void @@ -3046,105 +2785,105 @@ server_update_entries (file, update_dir, repository, updated) { if (!supported_response ("New-entry")) return; - buf_output0 (&protocol, "New-entry "); + buf_output0 (protocol, "New-entry "); output_dir (update_dir, repository); - buf_output0 (&protocol, file); - buf_output (&protocol, "\n", 1); + buf_output0 (protocol, file); + buf_output (protocol, "\n", 1); new_entries_line (); } - buf_send_counted (&protocol); + buf_send_counted (protocol); } static void serve_update (arg) char *arg; { - do_cvs_command (update); + do_cvs_command ("update", update); } static void serve_diff (arg) char *arg; { - do_cvs_command (diff); + do_cvs_command ("diff", diff); } static void serve_log (arg) char *arg; { - do_cvs_command (cvslog); + do_cvs_command ("cvslog", cvslog); } static void serve_add (arg) char *arg; { - do_cvs_command (add); + do_cvs_command ("add", add); } static void serve_remove (arg) char *arg; { - do_cvs_command (cvsremove); + do_cvs_command ("cvsremove", cvsremove); } static void serve_status (arg) char *arg; { - do_cvs_command (status); + do_cvs_command ("status", status); } static void serve_rdiff (arg) char *arg; { - do_cvs_command (patch); + do_cvs_command ("patch", patch); } static void serve_tag (arg) char *arg; { - do_cvs_command (tag); + do_cvs_command ("cvstag", cvstag); } static void serve_rtag (arg) char *arg; { - do_cvs_command (rtag); + do_cvs_command ("rtag", rtag); } static void serve_import (arg) char *arg; { - do_cvs_command (import); + do_cvs_command ("import", import); } static void serve_admin (arg) char *arg; { - do_cvs_command (admin); + do_cvs_command ("admin", admin); } static void serve_history (arg) char *arg; { - do_cvs_command (history); + do_cvs_command ("history", history); } static void serve_release (arg) char *arg; { - do_cvs_command (release); + do_cvs_command ("release", release); } static void serve_watch_on PROTO ((char *)); @@ -3153,7 +2892,7 @@ static void serve_watch_on (arg) char *arg; { - do_cvs_command (watch_on); + do_cvs_command ("watch_on", watch_on); } static void serve_watch_off PROTO ((char *)); @@ -3162,7 +2901,7 @@ static void serve_watch_off (arg) char *arg; { - do_cvs_command (watch_off); + do_cvs_command ("watch_off", watch_off); } static void serve_watch_add PROTO ((char *)); @@ -3171,7 +2910,7 @@ static void serve_watch_add (arg) char *arg; { - do_cvs_command (watch_add); + do_cvs_command ("watch_add", watch_add); } static void serve_watch_remove PROTO ((char *)); @@ -3180,7 +2919,7 @@ static void serve_watch_remove (arg) char *arg; { - do_cvs_command (watch_remove); + do_cvs_command ("watch_remove", watch_remove); } static void serve_watchers PROTO ((char *)); @@ -3189,7 +2928,7 @@ static void serve_watchers (arg) char *arg; { - do_cvs_command (watchers); + do_cvs_command ("watchers", watchers); } static void serve_editors PROTO ((char *)); @@ -3198,7 +2937,7 @@ static void serve_editors (arg) char *arg; { - do_cvs_command (editors); + do_cvs_command ("editors", editors); } static int noop PROTO ((int, char **)); @@ -3217,7 +2956,7 @@ static void serve_noop (arg) char *arg; { - do_cvs_command (noop); + do_cvs_command ("noop", noop); } static void serve_init PROTO ((char *)); @@ -3226,15 +2965,17 @@ static void serve_init (arg) char *arg; { - CVSroot = malloc (strlen (arg) + 1); - if (CVSroot == NULL) + if (!isabsolute (arg)) { - pending_error = ENOMEM; - return; + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E Root %s must be an absolute pathname", arg); + /* Fall through to do_cvs_command which will return the + actual error. */ } - strcpy (CVSroot, arg); + set_local_cvsroot (arg); - do_cvs_command (init); + do_cvs_command ("init", init); } static void serve_annotate PROTO ((char *)); @@ -3243,7 +2984,7 @@ static void serve_annotate (arg) char *arg; { - do_cvs_command (annotate); + do_cvs_command ("annotate", annotate); } static void @@ -3265,7 +3006,7 @@ serve_co (arg) tempdir = malloc (strlen (server_temp_dir) + 80); if (tempdir == NULL) { - printf ("E Out of memory\n"); + buf_output0 (buf_to_net, "E Out of memory\n"); return; } strcpy (tempdir, server_temp_dir); @@ -3273,22 +3014,34 @@ serve_co (arg) status = mkdir_p (tempdir); if (status != 0 && status != EEXIST) { - printf ("E Cannot create %s\n", tempdir); + buf_output0 (buf_to_net, "E Cannot create "); + buf_output0 (buf_to_net, tempdir); + buf_append_char (buf_to_net, '\n'); print_error (errno); free (tempdir); return; } - if (chdir (tempdir) < 0) + if ( CVS_CHDIR (tempdir) < 0) { - printf ("E Cannot change to directory %s\n", tempdir); + buf_output0 (buf_to_net, "E Cannot change to directory "); + buf_output0 (buf_to_net, tempdir); + buf_append_char (buf_to_net, '\n'); print_error (errno); free (tempdir); return; } free (tempdir); } - do_cvs_command (checkout); + + /* Compensate for server_export()'s setting of command_name. + * + * [It probably doesn't matter if do_cvs_command() gets "export" + * or "checkout", but we ought to be accurate where possible.] + */ + do_cvs_command ((strcmp (command_name, "export") == 0) ? + "export" : "checkout", + checkout); } static void @@ -3309,34 +3062,27 @@ server_copy_file (file, update_dir, repository, newfile) { if (!supported_response ("Copy-file")) return; - buf_output0 (&protocol, "Copy-file "); + buf_output0 (protocol, "Copy-file "); output_dir (update_dir, repository); - buf_output0 (&protocol, file); - buf_output0 (&protocol, "\n"); - buf_output0 (&protocol, newfile); - buf_output0 (&protocol, "\n"); + buf_output0 (protocol, file); + buf_output0 (protocol, "\n"); + buf_output0 (protocol, newfile); + buf_output0 (protocol, "\n"); } +/* See server.h for description. */ + void -server_updated (file, update_dir, repository, updated, file_info, checksum) - char *file; - char *update_dir; - char *repository; +server_updated (finfo, vers, updated, file_info, checksum) + struct file_info *finfo; + Vers_TS *vers; enum server_updated_arg4 updated; struct stat *file_info; unsigned char *checksum; { - char *short_pathname; - if (noexec) return; - short_pathname = xmalloc (strlen (update_dir) + strlen (file) + 10); - if (update_dir[0] == '\0') - strcpy (short_pathname, file); - else - sprintf (short_pathname, "%s/%s", update_dir, file); - if (entries_line != NULL && scratched_file == NULL) { FILE *f; @@ -3345,7 +3091,7 @@ server_updated (file, update_dir, repository, updated, file_info, checksum) unsigned long size; char size_text[80]; - if (stat (file, &sb) < 0) + if ( CVS_STAT (finfo->file, &sb) < 0) { if (existence_error (errno)) { @@ -3353,13 +3099,12 @@ server_updated (file, update_dir, repository, updated, file_info, checksum) * If we have a sticky tag for a branch on which the * file is dead, and cvs update the directory, it gets * a T_CHECKOUT but no file. So in this case just - * forget the whole thing. - */ + * forget the whole thing. */ free (entries_line); entries_line = NULL; goto done; } - error (1, errno, "reading %s", short_pathname); + error (1, errno, "reading %s", finfo->fullname); } if (checksum != NULL) @@ -3376,27 +3121,52 @@ server_updated (file, update_dir, repository, updated, file_info, checksum) int i; char buf[3]; - buf_output0 (&protocol, "Checksum "); + buf_output0 (protocol, "Checksum "); for (i = 0; i < 16; i++) { sprintf (buf, "%02x", (unsigned int) checksum[i]); - buf_output0 (&protocol, buf); + buf_output0 (protocol, buf); } - buf_append_char (&protocol, '\n'); + buf_append_char (protocol, '\n'); } } if (updated == SERVER_UPDATED) - buf_output0 (&protocol, "Updated "); + { + Node *node; + Entnode *entnode; + + if (!(supported_response ("Created") + && supported_response ("Update-existing"))) + buf_output0 (protocol, "Updated "); + else + { + assert (vers != NULL); + if (vers->ts_user == NULL) + buf_output0 (protocol, "Created "); + else + buf_output0 (protocol, "Update-existing "); + } + + /* Now munge the entries to say that the file is unmodified, + in case we end up processing it again (e.g. modules3-6 + in the testsuite). */ + node = findnode_fn (finfo->entries, finfo->file); + entnode = (Entnode *)node->data; + free (entnode->timestamp); + entnode->timestamp = xstrdup ("="); + } else if (updated == SERVER_MERGED) - buf_output0 (&protocol, "Merged "); + buf_output0 (protocol, "Merged "); else if (updated == SERVER_PATCHED) - buf_output0 (&protocol, "Patched "); + buf_output0 (protocol, "Patched "); + else if (updated == SERVER_RCS_DIFF) + buf_output0 (protocol, "Rcs-diff "); else abort (); - output_dir (update_dir, repository); - buf_output0 (&protocol, file); - buf_output (&protocol, "\n", 1); + output_dir (finfo->update_dir, finfo->repository); + buf_output0 (protocol, finfo->file); + buf_output (protocol, "\n", 1); new_entries_line (); @@ -3404,15 +3174,14 @@ server_updated (file, update_dir, repository, updated, file_info, checksum) char *mode_string; /* FIXME: When we check out files the umask of the server - (set in .bashrc if rsh is in use, or set in main.c in - the kerberos case, I think) affects what mode we send, - and it shouldn't. */ + (set in .bashrc if rsh is in use) affects what mode we + send, and it shouldn't. */ if (file_info != NULL) mode_string = mode_to_string (file_info->st_mode); else mode_string = mode_to_string (sb.st_mode); - buf_output0 (&protocol, mode_string); - buf_output0 (&protocol, "\n"); + buf_output0 (protocol, mode_string); + buf_output0 (protocol, "\n"); free (mode_string); } @@ -3424,7 +3193,7 @@ server_updated (file, update_dir, repository, updated, file_info, checksum) file we are sending. The client handles any line ending translation if necessary. */ - if (gzip_level + if (file_gzip_level /* * For really tiny files, the gzip process startup * time will outweigh the compression savings. This @@ -3436,51 +3205,51 @@ server_updated (file, update_dir, repository, updated, file_info, checksum) int status, fd, gzip_status; pid_t gzip_pid; - fd = open (file, O_RDONLY | OPEN_BINARY, 0); + fd = CVS_OPEN (finfo->file, O_RDONLY | OPEN_BINARY, 0); if (fd < 0) - error (1, errno, "reading %s", short_pathname); - fd = filter_through_gzip (fd, 1, gzip_level, &gzip_pid); + error (1, errno, "reading %s", finfo->fullname); + fd = filter_through_gzip (fd, 1, file_gzip_level, &gzip_pid); f = fdopen (fd, "rb"); status = buf_read_file_to_eof (f, &list, &last); size = buf_chain_length (list); if (status == -2) - (*protocol.memory_error) (&protocol); + (*protocol->memory_error) (protocol); else if (status != 0) error (1, ferror (f) ? errno : 0, "reading %s", - short_pathname); + finfo->fullname); if (fclose (f) == EOF) - error (1, errno, "reading %s", short_pathname); + error (1, errno, "reading %s", finfo->fullname); if (waitpid (gzip_pid, &gzip_status, 0) == -1) error (1, errno, "waiting for gzip process %ld", (long) gzip_pid); else if (gzip_status != 0) error (1, 0, "gzip exited %d", gzip_status); /* Prepending length with "z" is flag for using gzip here. */ - buf_output0 (&protocol, "z"); + buf_output0 (protocol, "z"); } else { long status; size = sb.st_size; - f = fopen (file, "rb"); + f = CVS_FOPEN (finfo->file, "rb"); if (f == NULL) - error (1, errno, "reading %s", short_pathname); + error (1, errno, "reading %s", finfo->fullname); status = buf_read_file (f, sb.st_size, &list, &last); if (status == -2) - (*protocol.memory_error) (&protocol); + (*protocol->memory_error) (protocol); else if (status != 0) error (1, ferror (f) ? errno : 0, "reading %s", - short_pathname); + finfo->fullname); if (fclose (f) == EOF) - error (1, errno, "reading %s", short_pathname); + error (1, errno, "reading %s", finfo->fullname); } } sprintf (size_text, "%lu\n", size); - buf_output0 (&protocol, size_text); + buf_output0 (protocol, size_text); - buf_append_data (&protocol, list, last); + buf_append_data (protocol, list, last); /* Note we only send a newline here if the file ended with one. */ /* @@ -3490,29 +3259,31 @@ server_updated (file, update_dir, repository, updated, file_info, checksum) * not up-to-date, and we indicate that by leaving the file there. * I'm thinking of cases like "cvs update foo/foo.c foo". */ - if ((updated == SERVER_UPDATED || updated == SERVER_PATCHED) + if ((updated == SERVER_UPDATED + || updated == SERVER_PATCHED + || updated == SERVER_RCS_DIFF) /* But if we are joining, we'll need the file when we call join_file. */ && !joining ()) - unlink (file); + CVS_UNLINK (finfo->file); } else if (scratched_file != NULL && entries_line == NULL) { - if (strcmp (scratched_file, file) != 0) + if (strcmp (scratched_file, finfo->file) != 0) error (1, 0, "CVS server internal error: `%s' vs. `%s' scratched", scratched_file, - file); + finfo->file); free (scratched_file); scratched_file = NULL; if (kill_scratched_file) - buf_output0 (&protocol, "Removed "); + buf_output0 (protocol, "Removed "); else - buf_output0 (&protocol, "Remove-entry "); - output_dir (update_dir, repository); - buf_output0 (&protocol, file); - buf_output (&protocol, "\n", 1); + buf_output0 (protocol, "Remove-entry "); + output_dir (finfo->update_dir, finfo->repository); + buf_output0 (protocol, finfo->file); + buf_output (protocol, "\n", 1); } else if (scratched_file == NULL && entries_line == NULL) { @@ -3524,9 +3295,16 @@ server_updated (file, update_dir, repository, updated, file_info, checksum) else error (1, 0, "CVS server internal error: Register *and* Scratch_Entry.\n"); - buf_send_counted (&protocol); - done: - free (short_pathname); + buf_send_counted (protocol); + done:; +} + +/* Return whether we should send patches in RCS format. */ + +int +server_use_rcs_diff () +{ + return supported_response ("Rcs-diff"); } void @@ -3539,10 +3317,10 @@ server_set_entstat (update_dir, repository) set_static_supported = supported_response ("Set-static-directory"); if (!set_static_supported) return; - buf_output0 (&protocol, "Set-static-directory "); + buf_output0 (protocol, "Set-static-directory "); output_dir (update_dir, repository); - buf_output0 (&protocol, "\n"); - buf_send_counted (&protocol); + buf_output0 (protocol, "\n"); + buf_send_counted (protocol); } void @@ -3558,20 +3336,24 @@ server_clear_entstat (update_dir, repository) if (noexec) return; - buf_output0 (&protocol, "Clear-static-directory "); + buf_output0 (protocol, "Clear-static-directory "); output_dir (update_dir, repository); - buf_output0 (&protocol, "\n"); - buf_send_counted (&protocol); + buf_output0 (protocol, "\n"); + buf_send_counted (protocol); } void -server_set_sticky (update_dir, repository, tag, date) +server_set_sticky (update_dir, repository, tag, date, nonbranch) char *update_dir; char *repository; char *tag; char *date; + int nonbranch; { static int set_sticky_supported = -1; + + assert (update_dir != NULL); + if (set_sticky_supported == -1) set_sticky_supported = supported_response ("Set-sticky"); if (!set_sticky_supported) return; @@ -3581,28 +3363,31 @@ server_set_sticky (update_dir, repository, tag, date) if (tag == NULL && date == NULL) { - buf_output0 (&protocol, "Clear-sticky "); + buf_output0 (protocol, "Clear-sticky "); output_dir (update_dir, repository); - buf_output0 (&protocol, "\n"); + buf_output0 (protocol, "\n"); } else { - buf_output0 (&protocol, "Set-sticky "); + buf_output0 (protocol, "Set-sticky "); output_dir (update_dir, repository); - buf_output0 (&protocol, "\n"); + buf_output0 (protocol, "\n"); if (tag != NULL) { - buf_output0 (&protocol, "T"); - buf_output0 (&protocol, tag); + if (nonbranch) + buf_output0 (protocol, "N"); + else + buf_output0 (protocol, "T"); + buf_output0 (protocol, tag); } else { - buf_output0 (&protocol, "D"); - buf_output0 (&protocol, date); + buf_output0 (protocol, "D"); + buf_output0 (protocol, date); } - buf_output0 (&protocol, "\n"); + buf_output0 (protocol, "\n"); } - buf_send_counted (&protocol); + buf_send_counted (protocol); } struct template_proc_data @@ -3629,11 +3414,11 @@ template_proc (repository, template) if (!supported_response ("Template")) /* Might want to warn the user that the rcsinfo feature won't work. */ return 0; - buf_output0 (&protocol, "Template "); + buf_output0 (protocol, "Template "); output_dir (data->update_dir, data->repository); - buf_output0 (&protocol, "\n"); + buf_output0 (protocol, "\n"); - fp = fopen (template, "rb"); + fp = CVS_FOPEN (template, "rb"); if (fp == NULL) { error (0, errno, "Couldn't open rcsinfo template file %s", template); @@ -3645,11 +3430,11 @@ template_proc (repository, template) return 1; } sprintf (buf, "%ld\n", (long) sb.st_size); - buf_output0 (&protocol, buf); + buf_output0 (protocol, buf); while (!feof (fp)) { n = fread (buf, 1, sizeof buf, fp); - buf_output (&protocol, buf, n); + buf_output (protocol, buf, n); if (ferror (fp)) { error (0, errno, "cannot read rcsinfo template file %s", template); @@ -3682,7 +3467,24 @@ serve_gzip_contents (arg) level = atoi (arg); if (level == 0) level = 6; - gzip_level = level; + file_gzip_level = level; +} + +static void +serve_gzip_stream (arg) + char *arg; +{ + int level; + level = atoi (arg); + if (level == 0) + level = 6; + + /* All further communication with the client will be compressed. */ + + buf_to_net = compress_buffer_initialize (buf_to_net, 0, level, + buf_to_net->memory_error); + buf_from_net = compress_buffer_initialize (buf_from_net, 1, level, + buf_from_net->memory_error); } static void @@ -3720,23 +3522,37 @@ expand_proc (pargc, argv, where, mwhere, mfile, shorten, if (mwhere != NULL) { - printf ("Module-expansion %s", mwhere); + buf_output0 (buf_to_net, "Module-expansion "); + buf_output0 (buf_to_net, mwhere); if (mfile != NULL) { - printf ("/%s", mfile); + buf_append_char (buf_to_net, '/'); + buf_output0 (buf_to_net, mfile); } - printf ("\n"); + buf_append_char (buf_to_net, '\n'); } else - { + { /* We may not need to do this anymore -- check the definition of aliases before removing */ if (*pargc == 1) - printf ("Module-expansion %s\n", dir); + { + buf_output0 (buf_to_net, "Module-expansion "); + buf_output0 (buf_to_net, dir); + buf_append_char (buf_to_net, '\n'); + } else - for (i = 1; i < *pargc; ++i) - printf ("Module-expansion %s/%s\n", dir, argv[i]); - } + { + for (i = 1; i < *pargc; ++i) + { + buf_output0 (buf_to_net, "Module-expansion "); + buf_output0 (buf_to_net, dir); + buf_append_char (buf_to_net, '/'); + buf_output0 (buf_to_net, argv[i]); + buf_append_char (buf_to_net, '\n'); + } + } + } return 0; } @@ -3749,11 +3565,6 @@ serve_expand_modules (arg) DBM *db; err = 0; - /* - * FIXME: error handling is bogus; do_module can write to stdout and/or - * stderr and we're not using do_cvs_command. - */ - server_expanding = 1; db = open_module (); for (i = 1; i < argument_count; i++) @@ -3775,9 +3586,13 @@ serve_expand_modules (arg) } if (err) /* We will have printed an error message already. */ - printf ("error \n"); + buf_output0 (buf_to_net, "error \n"); else - printf ("ok\n"); + buf_output0 (buf_to_net, "ok\n"); + + /* The client is waiting for the module expansions, so we must + send the output now. */ + buf_flush (buf_to_net, 1); } void @@ -3788,20 +3603,23 @@ server_prog (dir, name, which) { if (!supported_response ("Set-checkin-prog")) { - printf ("E \ + buf_output0 (buf_to_net, "E \ warning: this client does not support -i or -u flags in the modules file.\n"); return; } switch (which) { case PROG_CHECKIN: - printf ("Set-checkin-prog "); + buf_output0 (buf_to_net, "Set-checkin-prog "); break; case PROG_UPDATE: - printf ("Set-update-prog "); + buf_output0 (buf_to_net, "Set-update-prog "); break; } - printf ("%s\n%s\n", dir, name); + buf_output0 (buf_to_net, dir); + buf_append_char (buf_to_net, '\n'); + buf_output0 (buf_to_net, name); + buf_append_char (buf_to_net, '\n'); } static void @@ -3809,26 +3627,27 @@ serve_checkin_prog (arg) char *arg; { FILE *f; - f = fopen (CVSADM_CIPROG, "w+"); + f = CVS_FOPEN (CVSADM_CIPROG, "w+"); if (f == NULL) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_CIPROG)); - sprintf(pending_error_text, "E cannot open %s", CVSADM_CIPROG); + if (alloc_pending (80 + strlen (CVSADM_CIPROG))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_CIPROG); return; } if (fprintf (f, "%s\n", arg) < 0) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_CIPROG)); - sprintf(pending_error_text, "E cannot write to %s", CVSADM_CIPROG); + if (alloc_pending (80 + strlen (CVSADM_CIPROG))) + sprintf (pending_error_text, + "E cannot write to %s", CVSADM_CIPROG); return; } if (fclose (f) == EOF) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_CIPROG)); - sprintf(pending_error_text, "E cannot close %s", CVSADM_CIPROG); + if (alloc_pending (80 + strlen (CVSADM_CIPROG))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_CIPROG); return; } } @@ -3838,26 +3657,39 @@ serve_update_prog (arg) char *arg; { FILE *f; - f = fopen (CVSADM_UPROG, "w+"); + + /* Before we do anything we need to make sure we are not in readonly + mode. */ + if (!check_command_legal_p ("commit")) + { + /* I might be willing to make this a warning, except we lack the + machinery to do so. */ + if (alloc_pending (80)) + sprintf (pending_error_text, "\ +E Flag -u in modules not allowed in readonly mode"); + return; + } + + f = CVS_FOPEN (CVSADM_UPROG, "w+"); if (f == NULL) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_UPROG)); - sprintf(pending_error_text, "E cannot open %s", CVSADM_UPROG); + if (alloc_pending (80 + strlen (CVSADM_UPROG))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_UPROG); return; } if (fprintf (f, "%s\n", arg) < 0) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_UPROG)); - sprintf(pending_error_text, "E cannot write to %s", CVSADM_UPROG); + if (alloc_pending (80 + strlen (CVSADM_UPROG))) + sprintf (pending_error_text, "E cannot write to %s", CVSADM_UPROG); return; } if (fclose (f) == EOF) { pending_error = errno; - pending_error_text = malloc (80 + strlen(CVSADM_UPROG)); - sprintf(pending_error_text, "E cannot close %s", CVSADM_UPROG); + if (alloc_pending (80 + strlen (CVSADM_UPROG))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_UPROG); return; } } @@ -3884,8 +3716,8 @@ struct request requests[] = REQ_LINE("Root", serve_root, rq_essential), REQ_LINE("Valid-responses", serve_valid_responses, rq_essential), REQ_LINE("valid-requests", serve_valid_requests, rq_essential), - REQ_LINE("Repository", serve_repository, rq_essential), - REQ_LINE("Directory", serve_directory, rq_optional), + REQ_LINE("Repository", serve_repository, rq_optional), + REQ_LINE("Directory", serve_directory, rq_essential), REQ_LINE("Max-dotdot", serve_max_dotdot, rq_optional), REQ_LINE("Static-directory", serve_static_directory, rq_optional), REQ_LINE("Sticky", serve_sticky, rq_optional), @@ -3893,16 +3725,27 @@ struct request requests[] = REQ_LINE("Update-prog", serve_update_prog, rq_optional), REQ_LINE("Entry", serve_entry, rq_essential), REQ_LINE("Modified", serve_modified, rq_essential), - REQ_LINE("Lost", serve_lost, rq_optional), + REQ_LINE("Is-modified", serve_is_modified, rq_optional), + + /* The client must send this request to interoperate with CVS 1.5 + through 1.9 servers. The server must support it (although it can + be and is a noop) to interoperate with CVS 1.5 to 1.9 clients. */ REQ_LINE("UseUnchanged", serve_enable_unchanged, rq_enableme), - REQ_LINE("Unchanged", serve_unchanged, rq_optional), + + REQ_LINE("Unchanged", serve_unchanged, rq_essential), REQ_LINE("Notify", serve_notify, rq_optional), REQ_LINE("Questionable", serve_questionable, rq_optional), REQ_LINE("Case", serve_case, rq_optional), REQ_LINE("Argument", serve_argument, rq_essential), REQ_LINE("Argumentx", serve_argumentx, rq_essential), REQ_LINE("Global_option", serve_global_option, rq_optional), + REQ_LINE("Gzip-stream", serve_gzip_stream, rq_optional), REQ_LINE("Set", serve_set, rq_optional), +#ifdef ENCRYPTION +#ifdef HAVE_KERBEROS + REQ_LINE("Kerberos-encrypt", serve_kerberos_encrypt, rq_optional), +#endif +#endif REQ_LINE("expand-modules", serve_expand_modules, rq_optional), REQ_LINE("ci", serve_ci, rq_essential), REQ_LINE("co", serve_co, rq_essential), @@ -3946,11 +3789,20 @@ serve_valid_requests (arg) struct request *rq; if (print_pending_error ()) return; - printf ("Valid-requests"); + buf_output0 (buf_to_net, "Valid-requests"); for (rq = requests; rq->name != NULL; rq++) + { if (rq->func != NULL) - printf (" %s", rq->name); - printf ("\nok\n"); + { + buf_append_char (buf_to_net, ' '); + buf_output0 (buf_to_net, rq->name); + } + } + buf_output0 (buf_to_net, "\nok\n"); + + /* The client is waiting for the list of valid requests, so we + must send the output now. */ + buf_flush (buf_to_net, 1); } #ifdef sun @@ -3962,10 +3814,10 @@ static int command_pid_is_dead; static void wait_sig (sig) int sig; { - int status; - pid_t r = wait (&status); - if (r == command_pid) - command_pid_is_dead++; + int status; + pid_t r = wait (&status); + if (r == command_pid) + command_pid_is_dead++; } #endif @@ -3974,104 +3826,135 @@ server_cleanup (sig) int sig; { /* Do "rm -rf" on the temp directory. */ - int len; - char *cmd; - char *temp_dir; + int status; + int save_noexec; + + if (buf_to_net != NULL) + { + /* FIXME: If this is not the final call from server, this + could deadlock, because the client might be blocked writing + to us. This should not be a problem in practice, because + we do not generate much output when the client is not + waiting for it. */ + set_block (buf_to_net); + buf_flush (buf_to_net, 1); + + /* The calls to buf_shutdown are currently only meaningful + when we are using compression. First we shut down + BUF_FROM_NET. That will pick up the checksum generated + when the client shuts down its buffer. Then, after we have + generated any final output, we shut down BUF_TO_NET. */ + + status = buf_shutdown (buf_from_net); + if (status != 0) + { + error (0, status, "shutting down buffer from client"); + buf_flush (buf_to_net, 1); + } + } if (dont_delete_temp) + { + if (buf_to_net != NULL) + (void) buf_shutdown (buf_to_net); return; + } /* What a bogus kludge. This disgusting code makes all kinds of assumptions about SunOS, and is only for a bug in that system. So only enable it on Suns. */ #ifdef sun - if (command_pid > 0) { - /* To avoid crashes on SunOS due to bugs in SunOS tmpfs - triggered by the use of rename() in RCS, wait for the - subprocess to die. Unfortunately, this means draining output - while waiting for it to unblock the signal we sent it. Yuck! */ - int status; - pid_t r; - - signal (SIGCHLD, wait_sig); - if (sig) - /* Perhaps SIGTERM would be more correct. But the child - process will delay the SIGINT delivery until its own - children have exited. */ - kill (command_pid, SIGINT); - /* The caller may also have sent a signal to command_pid, so - always try waiting. First, though, check and see if it's still - there.... */ + if (command_pid > 0) + { + /* To avoid crashes on SunOS due to bugs in SunOS tmpfs + triggered by the use of rename() in RCS, wait for the + subprocess to die. Unfortunately, this means draining output + while waiting for it to unblock the signal we sent it. Yuck! */ + int status; + pid_t r; + + signal (SIGCHLD, wait_sig); + if (sig) + /* Perhaps SIGTERM would be more correct. But the child + process will delay the SIGINT delivery until its own + children have exited. */ + kill (command_pid, SIGINT); + /* The caller may also have sent a signal to command_pid, so + always try waiting. First, though, check and see if it's still + there.... */ do_waitpid: - r = waitpid (command_pid, &status, WNOHANG); - if (r == 0) - ; - else if (r == command_pid) - command_pid_is_dead++; - else if (r == -1) - switch (errno) { - case ECHILD: - command_pid_is_dead++; - break; - case EINTR: - goto do_waitpid; - } - else - /* waitpid should always return one of the above values */ - abort (); - while (!command_pid_is_dead) { - struct timeval timeout; - struct fd_set_wrapper readfds; - char buf[100]; - int i; - - /* Use a non-zero timeout to avoid eating up CPU cycles. */ - timeout.tv_sec = 2; - timeout.tv_usec = 0; - readfds = command_fds_to_drain; - switch (select (max_command_fd + 1, &readfds.fds, - (fd_set *)0, (fd_set *)0, - &timeout)) { - case -1: - if (errno != EINTR) + r = waitpid (command_pid, &status, WNOHANG); + if (r == 0) + ; + else if (r == command_pid) + command_pid_is_dead++; + else if (r == -1) + switch (errno) + { + case ECHILD: + command_pid_is_dead++; + break; + case EINTR: + goto do_waitpid; + } + else + /* waitpid should always return one of the above values */ abort (); - case 0: - /* timeout */ - break; - case 1: - for (i = 0; i <= max_command_fd; i++) + while (!command_pid_is_dead) + { + struct timeval timeout; + struct fd_set_wrapper readfds; + char buf[100]; + int i; + + /* Use a non-zero timeout to avoid eating up CPU cycles. */ + timeout.tv_sec = 2; + timeout.tv_usec = 0; + readfds = command_fds_to_drain; + switch (select (max_command_fd + 1, &readfds.fds, + (fd_set *)0, (fd_set *)0, + &timeout)) { - if (!FD_ISSET (i, &readfds.fds)) - continue; - /* this fd is non-blocking */ - while (read (i, buf, sizeof (buf)) >= 1) - ; + case -1: + if (errno != EINTR) + abort (); + case 0: + /* timeout */ + break; + case 1: + for (i = 0; i <= max_command_fd; i++) + { + if (!FD_ISSET (i, &readfds.fds)) + continue; + /* this fd is non-blocking */ + while (read (i, buf, sizeof (buf)) >= 1) + ; + } + break; + default: + abort (); } - break; - default: - abort (); } - } } #endif - /* This might be set by the user in ~/.bashrc, ~/.cshrc, etc. */ - temp_dir = getenv ("TMPDIR"); - if (temp_dir == NULL || temp_dir[0] == '\0') - temp_dir = "/tmp"; - chdir(temp_dir); - - len = strlen (server_temp_dir) + 80; - cmd = malloc (len); - if (cmd == NULL) - { - printf ("E Cannot delete %s on server; out of memory\n", - server_temp_dir); - return; - } - sprintf (cmd, "rm -rf %s", server_temp_dir); - system (cmd); - free (cmd); + CVS_CHDIR (Tmpdir); + /* Temporarily clear noexec, so that we clean up our temp directory + regardless of it (this could more cleanly be handled by moving + the noexec check to all the unlink_file_dir callers from + unlink_file_dir itself). */ + save_noexec = noexec; + noexec = 0; + /* FIXME? Would be nice to not ignore errors. But what should we do? + We could try to do this before we shut down the network connection, + and try to notify the client (but the client might not be waiting + for responses). We could try something like syslog() or our own + log file. */ + unlink_file_dir (orig_server_temp_dir); + noexec = save_noexec; + + if (buf_to_net != NULL) + (void) buf_shutdown (buf_to_net); } int server_active = 0; @@ -4094,6 +3977,13 @@ server (argc, argv) } /* Ignore argc and argv. They might be from .cvsrc. */ + buf_to_net = fd_buffer_initialize (STDOUT_FILENO, 0, + outbuf_memory_error); + buf_from_net = stdio_buffer_initialize (stdin, 1, outbuf_memory_error); + + saved_output = buf_nonio_initialize (outbuf_memory_error); + saved_outerr = buf_nonio_initialize (outbuf_memory_error); + /* Since we're in the server parent process, error should use the protocol to report error messages. */ error_use_protocol = 1; @@ -4125,6 +4015,17 @@ server (argc, argv) { printf ("E Fatal server error, aborting.\n\ error ENOMEM Virtual memory exhausted.\n"); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } putenv (env); @@ -4135,46 +4036,115 @@ error ENOMEM Virtual memory exhausted.\n"); { char *p; - /* This might be set by the user in ~/.bashrc, ~/.cshrc, etc. */ - char *temp_dir = getenv ("TMPDIR"); - if (temp_dir == NULL || temp_dir[0] == '\0') - temp_dir = "/tmp"; - - server_temp_dir = malloc (strlen (temp_dir) + 80); - if (server_temp_dir == NULL) + /* The code which wants to chdir into server_temp_dir is not set + up to deal with it being a relative path. So give an error + for that case. */ + if (!isabsolute (Tmpdir)) { + pending_error_text = malloc (80 + strlen (Tmpdir)); + if (pending_error_text == NULL) + { + pending_error = ENOMEM; + } + else + { + sprintf (pending_error_text, + "E Value of %s for TMPDIR is not absolute", Tmpdir); + } + /* FIXME: we would like this error to be persistent, that + is, not cleared by print_pending_error. The current client + will exit as soon as it gets an error, but the protocol spec + does not require a client to do so. */ + } + else + { + int status; + + server_temp_dir = malloc (strlen (Tmpdir) + 80); + if (server_temp_dir == NULL) + { + /* + * Strictly speaking, we're not supposed to output anything + * now. But we're about to exit(), give it a try. + */ + printf ("E Fatal server error, aborting.\n\ +error ENOMEM Virtual memory exhausted.\n"); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket + subsystems on NT and OS2 or dealing with windows + and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + + exit (EXIT_FAILURE); + } + strcpy (server_temp_dir, Tmpdir); + + /* Remove a trailing slash from TMPDIR if present. */ + p = server_temp_dir + strlen (server_temp_dir) - 1; + if (*p == '/') + *p = '\0'; + /* - * Strictly speaking, we're not supposed to output anything - * now. But we're about to exit(), give it a try. + * I wanted to use cvs-serv/PID, but then you have to worry about + * the permissions on the cvs-serv directory being right. So + * use cvs-servPID. */ - printf ("E Fatal server error, aborting.\n\ -error ENOMEM Virtual memory exhausted.\n"); - exit (EXIT_FAILURE); - } - strcpy (server_temp_dir, temp_dir); + strcat (server_temp_dir, "/cvs-serv"); - /* Remove a trailing slash from TMPDIR if present. */ - p = server_temp_dir + strlen (server_temp_dir) - 1; - if (*p == '/') - *p = '\0'; + p = server_temp_dir + strlen (server_temp_dir); + sprintf (p, "%ld", (long) getpid ()); - /* - * I wanted to use cvs-serv/PID, but then you have to worry about - * the permissions on the cvs-serv directory being right. So - * use cvs-servPID. - */ - strcat (server_temp_dir, "/cvs-serv"); + orig_server_temp_dir = server_temp_dir; - p = server_temp_dir + strlen (server_temp_dir); - sprintf (p, "%ld", (long) getpid ()); + /* Create the temporary directory, and set the mode to + 700, to discourage random people from tampering with + it. */ + status = mkdir_p (server_temp_dir); + if (status != 0 && status != EEXIST) + { + if (alloc_pending (80)) + strcpy (pending_error_text, + "E can't create temporary directory"); + pending_error = status; + } +#ifndef CHMOD_BROKEN + else + { + if (chmod (server_temp_dir, S_IRWXU) < 0) + { + int save_errno = errno; + if (alloc_pending (80)) + strcpy (pending_error_text, "\ +E cannot change permissions on temporary directory"); + pending_error = save_errno; + } + } +#endif + } } +#ifdef SIGHUP (void) SIG_register (SIGHUP, server_cleanup); +#endif +#ifdef SIGINT (void) SIG_register (SIGINT, server_cleanup); +#endif +#ifdef SIGQUIT (void) SIG_register (SIGQUIT, server_cleanup); +#endif +#ifdef SIGPIPE (void) SIG_register (SIGPIPE, server_cleanup); +#endif +#ifdef SIGTERM (void) SIG_register (SIGTERM, server_cleanup); - +#endif + /* Now initialize our argument vector (for arguments from the client). */ /* Small for testing. */ @@ -4189,17 +4159,30 @@ error ENOMEM Virtual memory exhausted.\n"); */ printf ("E Fatal server error, aborting.\n\ error ENOMEM Virtual memory exhausted.\n"); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } argument_count = 1; - argument_vector[0] = "Dummy argument 0"; - - buf_to_net.data = buf_to_net.last = NULL; - buf_to_net.fd = STDOUT_FILENO; - buf_to_net.output = 1; - buf_to_net.nonblocking = 0; - buf_to_net.memory_error = outbuf_memory_error; + /* This gets printed if the client supports an option which the + server doesn't, causing the server to print a usage message. + FIXME: probably should be using program_name here. + FIXME: just a nit, I suppose, but the usage message the server + prints isn't literally true--it suggests "cvs server" followed + by options which are for a particular command. Might be nice to + say something like "client apparently supports an option not supported + by this server" or something like that instead of usage message. */ + argument_vector[0] = "cvs server"; server_active = 1; @@ -4207,16 +4190,19 @@ error ENOMEM Virtual memory exhausted.\n"); { char *cmd, *orig_cmd; struct request *rq; + int status; - orig_cmd = cmd = read_line (stdin); - if (cmd == NULL) - break; - if (cmd == NO_MEM_ERROR) + status = buf_read_line (buf_from_net, &cmd, (int *) NULL); + if (status == -2) { - printf ("E Fatal server error, aborting.\n\ + buf_output0 (buf_to_net, "E Fatal server error, aborting.\n\ error ENOMEM Virtual memory exhausted.\n"); break; } + if (status != 0) + break; + + orig_cmd = cmd; for (rq = requests; rq->name != NULL; ++rq) if (strncmp (cmd, rq->name, strlen (rq->name)) == 0) { @@ -4238,7 +4224,12 @@ error ENOMEM Virtual memory exhausted.\n"); if (rq->name == NULL) { if (!print_pending_error ()) - printf ("error unrecognized request `%s'\n", cmd); + { + buf_output0 (buf_to_net, "error unrecognized request `"); + buf_output0 (buf_to_net, cmd); + buf_append_char (buf_to_net, '\''); + buf_append_char (buf_to_net, '\n'); + } } free (orig_cmd); } @@ -4247,52 +4238,118 @@ error ENOMEM Virtual memory exhausted.\n"); } -#ifdef AUTH_SERVER_SUPPORT +#if defined (HAVE_KERBEROS) || defined (AUTH_SERVER_SUPPORT) +static void switch_to_user PROTO((const char *)); -extern char *crypt PROTO((const char *, const char *)); +static void +switch_to_user (username) + const char *username; +{ + struct passwd *pw; + + pw = getpwnam (username); + if (pw == NULL) + { + printf ("E Fatal error, aborting.\n\ +error 0 %s: no such user\n", username); + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + + exit (EXIT_FAILURE); + } + +#if HAVE_INITGROUPS + initgroups (pw->pw_name, pw->pw_gid); +#endif /* HAVE_INITGROUPS */ -/* This was test code, which we may need again. */ -#if 0 - /* If we were invoked this way, then stdin comes from the - client and stdout/stderr writes to it. */ - int c; - while ((c = getc (stdin)) != EOF && c != '*') +#ifdef SETXID_SUPPORT + /* honor the setgid bit iff set*/ + if (getgid() != getegid()) { - printf ("%c", toupper (c)); - fflush (stdout); + setgid (getegid ()); } - exit (0); -#endif /* 1/0 */ + else +#else + { + setgid (pw->pw_gid); + } +#endif + + setuid (pw->pw_uid); + /* We don't want our umask to change file modes. The modes should + be set by the modes used in the repository, and by the umask of + the client. */ + umask (0); + +#if HAVE_PUTENV + /* Set LOGNAME and USER in the environment, in case they are + already set to something else. */ + { + char *env; + + env = xmalloc (sizeof "LOGNAME=" + strlen (username)); + (void) sprintf (env, "LOGNAME=%s", username); + (void) putenv (env); + + env = xmalloc (sizeof "USER=" + strlen (username)); + (void) sprintf (env, "USER=%s", username); + (void) putenv (env); + } +#endif /* HAVE_PUTENV */ +} +#endif + +#ifdef AUTH_SERVER_SUPPORT + +extern char *crypt PROTO((const char *, const char *)); /* * 0 means no entry found for this user. * 1 means entry found and password matches. * 2 means entry found, but password does not match. + * + * If success, host_user_ptr will be set to point at the system + * username (i.e., the "real" identity, which may or may not be the + * CVS username) of this user; caller may free this. Global + * CVS_Username will point at an allocated copy of cvs username (i.e., + * the username argument below). */ -int +static int check_repository_password (username, password, repository, host_user_ptr) char *username, *password, *repository, **host_user_ptr; { int retval = 0; FILE *fp; char *filename; - char *linebuf; + char *linebuf = NULL; + size_t linebuf_len; int found_it = 0; int namelen; + /* We don't use CVSroot_directory because it hasn't been set yet + * -- our `repository' argument came from the authentication + * protocol, not the regular CVS protocol. + */ + filename = xmalloc (strlen (repository) + 1 - + strlen ("CVSROOT") + + strlen (CVSROOTADM) + 1 - + strlen ("passwd") + + strlen (CVSROOTADM_PASSWD) + 1); - strcpy (filename, repository); - strcat (filename, "/CVSROOT"); - strcat (filename, "/passwd"); - - fp = fopen (filename, "r"); + (void) sprintf (filename, "%s/%s/%s", repository, + CVSROOTADM, CVSROOTADM_PASSWD); + + fp = CVS_FOPEN (filename, "r"); if (fp == NULL) { if (!existence_error (errno)) @@ -4302,27 +4359,14 @@ check_repository_password (username, password, repository, host_user_ptr) /* Look for a relevant line -- one with this user's name. */ namelen = strlen (username); - while (1) + while (getline (&linebuf, &linebuf_len, fp) >= 0) { - linebuf = read_line(fp); - if (linebuf == NULL) - { - free (linebuf); - break; - } - if (linebuf == NO_MEM_ERROR) - { - error (0, errno, "out of memory"); - break; - } if ((strncmp (linebuf, username, namelen) == 0) && (linebuf[namelen] == ':')) { found_it = 1; break; } - free (linebuf); - } if (ferror (fp)) error (0, errno, "cannot read %s", filename); @@ -4332,16 +4376,25 @@ check_repository_password (username, password, repository, host_user_ptr) /* If found_it != 0, then linebuf contains the information we need. */ if (found_it) { - char *found_password; + char *found_password, *host_user_tmp; strtok (linebuf, ":"); found_password = strtok (NULL, ": \n"); - *host_user_ptr = strtok (NULL, ": \n"); - if (*host_user_ptr == NULL) *host_user_ptr = username; + host_user_tmp = strtok (NULL, ": \n"); + if (host_user_tmp == NULL) + host_user_tmp = username; + if (strcmp (found_password, crypt (password, found_password)) == 0) + { + /* Give host_user_ptr permanent storage. */ + *host_user_ptr = xstrdup (host_user_tmp); retval = 1; + } else - retval = 2; + { + *host_user_ptr = NULL; + retval = 2; + } } else { @@ -4350,18 +4403,20 @@ check_repository_password (username, password, repository, host_user_ptr) } free (filename); + if (linebuf) + free (linebuf); return retval; } /* Return a hosting username if password matches, else NULL. */ -char * +static char * check_password (username, password, repository) char *username, *password, *repository; { int rc; - char *host_user; + char *host_user = NULL; /* First we see if this user has a password in the CVS-specific password file. If so, that's enough to authenticate with. If @@ -4370,229 +4425,760 @@ check_password (username, password, repository) rc = check_repository_password (username, password, repository, &host_user); + if (rc == 2) + return NULL; + + /* else */ + if (rc == 1) - return host_user; - else if (rc == 2) - return 0; + { + /* host_user already set by reference, so just return. */ + goto handle_return; + } else if (rc == 0) { /* No cvs password found, so try /etc/passwd. */ + const char *found_passwd = NULL; +#ifdef HAVE_GETSPNAM + struct spwd *pw; + + pw = getspnam (username); + if (pw != NULL) + { + found_passwd = pw->sp_pwdp; + } +#else struct passwd *pw; - char *found_passwd; pw = getpwnam (username); + if (pw != NULL) + { + found_passwd = pw->pw_passwd; + } +#endif + if (pw == NULL) - { + { printf ("E Fatal error, aborting.\n\ error 0 %s: no such user\n", username); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } - found_passwd = pw->pw_passwd; - + if (found_passwd && *found_passwd) - return ((! strcmp (found_passwd, crypt (password, found_passwd))) - ? username : NULL); + { + host_user = ((! strcmp (found_passwd, + crypt (password, found_passwd))) + ? username : NULL); + goto handle_return; + } else if (password && *password) - return username; + { + host_user = username; + goto handle_return; + } else - return NULL; + { + host_user = NULL; + goto handle_return; + } } else { /* Something strange happened. We don't know what it was, but we certainly won't grant authorization. */ - return NULL; + host_user = NULL; + goto handle_return; + } + +handle_return: + if (host_user) + { + /* Set CVS_Username here, in allocated space. + It might or might not be the same as host_user. */ + CVS_Username = xmalloc (strlen (username) + 1); + strcpy (CVS_Username, username); } -} + return host_user; +} /* Read username and password from client (i.e., stdin). If correct, then switch to run as that user and send an ACK to the client via stdout, else send NACK and die. */ void -authenticate_connection () +pserver_authenticate_connection () { - char tmp[PATH_MAX]; - char repository[PATH_MAX]; - char username[PATH_MAX]; - char password[PATH_MAX]; - char *host_user; - char *descrambled_password; - struct passwd *pw; - int verify_and_exit = 0; - - /* The Authentication Protocol. Client sends: - * - * BEGIN AUTH REQUEST\n - * <REPOSITORY>\n - * <USERNAME>\n - * <PASSWORD>\n - * END AUTH REQUEST\n - * - * Server uses above information to authenticate, then sends - * - * I LOVE YOU\n - * - * if it grants access, else - * - * I HATE YOU\n - * - * if it denies access (and it exits if denying). - * - * When the client is "cvs login", the user does not desire actual - * repository access, but would like to confirm the password with - * the server. In this case, the start and stop strings are - * - * BEGIN VERIFICATION REQUEST\n - * - * and - * - * END VERIFICATION REQUEST\n - * - * On a verification request, the server's responses are the same - * (with the obvious semantics), but it exits immediately after - * sending the response in both cases. - * - * Why is the repository sent? Well, note that the actual - * client/server protocol can't start up until authentication is - * successful. But in order to perform authentication, the server - * needs to look up the password in the special CVS passwd file, - * before trying /etc/passwd. So the client transmits the - * repository as part of the "authentication protocol". The - * repository will be redundantly retransmitted later, but that's no - * big deal. - */ - - /* Since we're in the server parent process, error should use the - protocol to report error messages. */ - error_use_protocol = 1; - - /* Make sure the protocol starts off on the right foot... */ - fgets (tmp, PATH_MAX, stdin); - if (strcmp (tmp, "BEGIN VERIFICATION REQUEST\n") == 0) - verify_and_exit = 1; - else if (strcmp (tmp, "BEGIN AUTH REQUEST\n") != 0) - error (1, 0, "bad auth protocol start: %s", tmp); - - /* Get the three important pieces of information in order. */ - fgets (repository, PATH_MAX, stdin); - fgets (username, PATH_MAX, stdin); - fgets (password, PATH_MAX, stdin); - - /* Make them pure. */ - strip_trailing_newlines (repository); - strip_trailing_newlines (username); - strip_trailing_newlines (password); - - /* ... and make sure the protocol ends on the right foot. */ - fgets (tmp, PATH_MAX, stdin); - if (strcmp (tmp, - verify_and_exit ? - "END VERIFICATION REQUEST\n" : "END AUTH REQUEST\n") - != 0) - { - error (1, 0, "bad auth protocol end: %s", tmp); - } - - /* We need the real cleartext before we hash it. */ - descrambled_password = descramble (password); - host_user = check_password (username, descrambled_password, repository); - if (host_user) - { - printf ("I LOVE YOU\n"); - fflush (stdout); - memset (descrambled_password, 0, strlen (descrambled_password)); - free (descrambled_password); - } - else - { - printf ("I HATE YOU\n"); - fflush (stdout); - memset (descrambled_password, 0, strlen (descrambled_password)); - free (descrambled_password); - exit (EXIT_FAILURE); - } - - /* Don't go any farther if we're just responding to "cvs login". */ - if (verify_and_exit) - exit (0); - - /* Switch to run as this user. */ - pw = getpwnam (host_user); - if (pw == NULL) - { - error (1, 0, - "fatal error, aborting.\nerror 0 %s: no such user\n", - username); - } - -#if HAVE_INITGROUPS - initgroups (pw->pw_name, pw->pw_gid); -#endif /* HAVE_INITGROUPS */ + char *tmp = NULL; + size_t tmp_allocated = 0; + char *repository = NULL; + size_t repository_allocated = 0; + char *username = NULL; + size_t username_allocated = 0; + char *password = NULL; + size_t password_allocated = 0; - setgid (pw->pw_gid); - setuid (pw->pw_uid); - /* Inhibit access by randoms. Don't want people randomly - changing our temporary tree before we check things in. */ - umask (077); - -#if HAVE_PUTENV - /* Set LOGNAME and USER in the environment, in case they are - already set to something else. */ - { - char *env; - - env = xmalloc (sizeof "LOGNAME=" + strlen (username)); - (void) sprintf (env, "LOGNAME=%s", username); - (void) putenv (env); - - env = xmalloc (sizeof "USER=" + strlen (username)); - (void) sprintf (env, "USER=%s", username); - (void) putenv (env); - } -#endif /* HAVE_PUTENV */ + char *host_user; + char *descrambled_password; + int verify_and_exit = 0; + + /* The Authentication Protocol. Client sends: + * + * BEGIN AUTH REQUEST\n + * <REPOSITORY>\n + * <USERNAME>\n + * <PASSWORD>\n + * END AUTH REQUEST\n + * + * Server uses above information to authenticate, then sends + * + * I LOVE YOU\n + * + * if it grants access, else + * + * I HATE YOU\n + * + * if it denies access (and it exits if denying). + * + * When the client is "cvs login", the user does not desire actual + * repository access, but would like to confirm the password with + * the server. In this case, the start and stop strings are + * + * BEGIN VERIFICATION REQUEST\n + * + * and + * + * END VERIFICATION REQUEST\n + * + * On a verification request, the server's responses are the same + * (with the obvious semantics), but it exits immediately after + * sending the response in both cases. + * + * Why is the repository sent? Well, note that the actual + * client/server protocol can't start up until authentication is + * successful. But in order to perform authentication, the server + * needs to look up the password in the special CVS passwd file, + * before trying /etc/passwd. So the client transmits the + * repository as part of the "authentication protocol". The + * repository will be redundantly retransmitted later, but that's no + * big deal. + */ + +#ifdef SO_KEEPALIVE + /* Set SO_KEEPALIVE on the socket, so that we don't hang forever + if the client dies while we are waiting for input. */ + { + int on = 1; + + (void) setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof on); + } +#endif + + /* Make sure the protocol starts off on the right foot... */ + if (getline (&tmp, &tmp_allocated, stdin) < 0) + /* FIXME: what? We could try writing error/eof, but chances + are the network connection is dead bidirectionally. log it + somewhere? */ + ; + + if (strcmp (tmp, "BEGIN VERIFICATION REQUEST\n") == 0) + verify_and_exit = 1; + else if (strcmp (tmp, "BEGIN AUTH REQUEST\n") != 0) + error (1, 0, "bad auth protocol start: %s", tmp); + + /* Get the three important pieces of information in order. */ + /* See above comment about error handling. */ + getline (&repository, &repository_allocated, stdin); + getline (&username, &username_allocated, stdin); + getline (&password, &password_allocated, stdin); + + /* Make them pure. */ + strip_trailing_newlines (repository); + strip_trailing_newlines (username); + strip_trailing_newlines (password); + + /* ... and make sure the protocol ends on the right foot. */ + /* See above comment about error handling. */ + getline (&tmp, &tmp_allocated, stdin); + if (strcmp (tmp, + verify_and_exit ? + "END VERIFICATION REQUEST\n" : "END AUTH REQUEST\n") + != 0) + { + error (1, 0, "bad auth protocol end: %s", tmp); + } + + /* We need the real cleartext before we hash it. */ + descrambled_password = descramble (password); + host_user = check_password (username, descrambled_password, repository); + memset (descrambled_password, 0, strlen (descrambled_password)); + free (descrambled_password); + if (host_user) + { + printf ("I LOVE YOU\n"); + fflush (stdout); + } + else + { + printf ("I HATE YOU\n"); + fflush (stdout); + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + + exit (EXIT_FAILURE); + } + + /* Don't go any farther if we're just responding to "cvs login". */ + if (verify_and_exit) + { +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + + exit (0); + } + + /* Set Pserver_Repos so that we can check later that the same + repository is sent in later client/server protocol. */ + Pserver_Repos = xmalloc (strlen (repository) + 1); + strcpy (Pserver_Repos, repository); + + /* Switch to run as this user. */ + switch_to_user (host_user); + free (tmp); + free (repository); + free (username); + free (password); } #endif /* AUTH_SERVER_SUPPORT */ +#ifdef HAVE_KERBEROS +void +kserver_authenticate_connection () +{ + int status; + char instance[INST_SZ]; + struct sockaddr_in peer; + struct sockaddr_in laddr; + int len; + KTEXT_ST ticket; + AUTH_DAT auth; + char version[KRB_SENDAUTH_VLEN]; + char user[ANAME_SZ]; + + strcpy (instance, "*"); + len = sizeof peer; + if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0 + || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr, + &len) < 0) + { + printf ("E Fatal error, aborting.\n\ +error %s getpeername or getsockname failed\n", strerror (errno)); +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); + } + +#ifdef SO_KEEPALIVE + /* Set SO_KEEPALIVE on the socket, so that we don't hang forever + if the client dies while we are waiting for input. */ + { + int on = 1; + + (void) setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof on); + } +#endif + + status = krb_recvauth (KOPT_DO_MUTUAL, STDIN_FILENO, &ticket, "rcmd", + instance, &peer, &laddr, &auth, "", sched, + version); + if (status != KSUCCESS) + { + printf ("E Fatal error, aborting.\n\ +error 0 kerberos: %s\n", krb_get_err_text(status)); +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); + } + + memcpy (kblock, auth.session, sizeof (C_Block)); + + /* Get the local name. */ + status = krb_kntoln (&auth, user); + if (status != KSUCCESS) + { + printf ("E Fatal error, aborting.\n\ +error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status)); +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); + } + + /* Switch to run as this user. */ + switch_to_user (user); +} +#endif /* HAVE_KERBEROS */ + #endif /* SERVER_SUPPORT */ +#if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) + +/* This global variable is non-zero if the user requests encryption on + the command line. */ +int cvsencrypt; + +#ifdef ENCRYPTION + +#ifdef HAVE_KERBEROS + +/* An encryption interface using Kerberos. This is built on top of + the buffer structure. We encrypt using a big endian two byte count + field followed by a block of encrypted data. */ + +/* This structure is the closure field of a Kerberos encryption + buffer. */ + +struct krb_encrypt_buffer +{ + /* The underlying buffer. */ + struct buffer *buf; + /* The Kerberos key schedule. */ + Key_schedule sched; + /* The Kerberos DES block. */ + C_Block block; + /* For an input buffer, we may have to buffer up data here. */ + /* This is non-zero if the buffered data is decrypted. Otherwise, + the buffered data is encrypted, and starts with the two byte + count. */ + int clear; + /* The amount of buffered data. */ + int holdsize; + /* The buffer allocated to hold the data. */ + char *holdbuf; + /* The size of holdbuf. */ + int holdbufsize; + /* If clear is set, we need another data pointer to track where we + are in holdbuf. If clear is zero, then this pointer is not + used. */ + char *holddata; +}; + +static int krb_encrypt_buffer_input PROTO((void *, char *, int, int, int *)); +static int krb_encrypt_buffer_output PROTO((void *, const char *, int, int *)); +static int krb_encrypt_buffer_flush PROTO((void *)); +static int krb_encrypt_buffer_block PROTO((void *, int)); +static int krb_encrypt_buffer_shutdown PROTO((void *)); + +/* Create an encryption buffer. */ + +struct buffer * +krb_encrypt_buffer_initialize (buf, input, sched, block, memory) + struct buffer *buf; + int input; + Key_schedule sched; + C_Block block; + void (*memory) PROTO((struct buffer *)); +{ + struct krb_encrypt_buffer *kb; + + kb = (struct krb_encrypt_buffer *) xmalloc (sizeof *kb); + memset (kb, 0, sizeof *kb); + + kb->buf = buf; + memcpy (kb->sched, sched, sizeof (Key_schedule)); + memcpy (kb->block, block, sizeof (C_Block)); + if (input) + { + /* We add some space to the buffer to hold the length. */ + kb->holdbufsize = BUFFER_DATA_SIZE + 16; + kb->holdbuf = xmalloc (kb->holdbufsize); + } + + return buf_initialize (input ? krb_encrypt_buffer_input : NULL, + input ? NULL : krb_encrypt_buffer_output, + input ? NULL : krb_encrypt_buffer_flush, + krb_encrypt_buffer_block, + krb_encrypt_buffer_shutdown, + memory, + kb); +} + +/* Input data from a Kerberos encryption buffer. */ + +static int +krb_encrypt_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; + + *got = 0; + + if (kb->holdsize > 0 && kb->clear) + { + int copy; + + copy = kb->holdsize; + + if (copy > size) + { + memcpy (data, kb->holddata, size); + kb->holdsize -= size; + kb->holddata += size; + *got = size; + return 0; + } + + memcpy (data, kb->holddata, copy); + kb->holdsize = 0; + kb->clear = 0; + + data += copy; + need -= copy; + size -= copy; + *got = copy; + } + + while (need > 0 || *got == 0) + { + int get, status, nread, count, dcount; + char *bytes; + char stackoutbuf[BUFFER_DATA_SIZE + 16]; + char *outbuf; + + /* If we don't already have the two byte count, get it. */ + if (kb->holdsize < 2) + { + get = 2 - kb->holdsize; + status = buf_read_data (kb->buf, get, &bytes, &nread); + if (status != 0) + { + /* buf_read_data can return -2, but a buffer input + function is only supposed to return -1, 0, or an + error code. */ + if (status == -2) + status = ENOMEM; + return status; + } + + if (nread == 0) + { + /* The buffer is in nonblocking mode, and we didn't + manage to read anything. */ + return 0; + } + + if (get == 1) + kb->holdbuf[1] = bytes[0]; + else + { + kb->holdbuf[0] = bytes[0]; + if (nread < 2) + { + /* We only got one byte, but we needed two. Stash + the byte we got, and try again. */ + kb->holdsize = 1; + continue; + } + kb->holdbuf[1] = bytes[1]; + } + kb->holdsize = 2; + } + + /* Read the encrypted block of data. */ + + count = (((kb->holdbuf[0] & 0xff) << 8) + + (kb->holdbuf[1] & 0xff)); + + if (count + 2 > kb->holdbufsize) + { + char *n; + + /* This should be impossible, since we should have + allocated space for the largest possible block in the + initialize function. However, we handle it just in + case something changes in the future, so that a current + server can handle a later client. */ + + n = realloc (kb->holdbuf, count + 2); + if (n == NULL) + { + (*kb->buf->memory_error) (kb->buf); + return ENOMEM; + } + kb->holdbuf = n; + kb->holdbufsize = count + 2; + } + + get = count - (kb->holdsize - 2); + + status = buf_read_data (kb->buf, get, &bytes, &nread); + if (status != 0) + { + /* buf_read_data can return -2, but a buffer input + function is only supposed to return -1, 0, or an error + code. */ + if (status == -2) + status = ENOMEM; + return status; + } + + if (nread == 0) + { + /* We did not get any data. Presumably the buffer is in + nonblocking mode. */ + return 0; + } + + /* FIXME: We could complicate the code here to avoid this + memcpy in the common case of kb->holdsize == 2 && nread == + get. */ + memcpy (kb->holdbuf + kb->holdsize, bytes, nread); + kb->holdsize += nread; + + if (nread < get) + { + /* We did not get all the data we need. buf_read_data + does not promise to return all the bytes requested, so + we must try again. */ + continue; + } + + /* We have a complete encrypted block of COUNT bytes at + KB->HOLDBUF + 2. Decrypt it. */ + + if (count <= sizeof stackoutbuf) + outbuf = stackoutbuf; + else + { + /* I believe this is currently impossible, but we handle + it for the benefit of future client changes. */ + outbuf = malloc (count); + if (outbuf == NULL) + { + (*kb->buf->memory_error) (kb->buf); + return ENOMEM; + } + } + + des_cbc_encrypt ((C_Block *) (kb->holdbuf + 2), (C_Block *) outbuf, + count, kb->sched, &kb->block, 0); + + /* The first two bytes in the decrypted buffer are the real + (unaligned) length. */ + dcount = ((outbuf[0] & 0xff) << 8) + (outbuf[1] & 0xff); + + if (((dcount + 2 + 7) & ~7) != count) + error (1, 0, "Decryption failure"); + + if (dcount > size) + { + /* We have too much data for the buffer. We need to save + some of it for the next call. */ + + memcpy (data, outbuf + 2, size); + *got += size; + + kb->holdsize = dcount - size; + memcpy (kb->holdbuf, outbuf + 2 + size, dcount - size); + kb->holddata = kb->holdbuf; + kb->clear = 1; + + if (outbuf != stackoutbuf) + free (outbuf); + + return 0; + } + + memcpy (data, outbuf + 2, dcount); + + if (outbuf != stackoutbuf) + free (outbuf); + + kb->holdsize = 0; + + data += dcount; + need -= dcount; + size -= dcount; + *got += dcount; + } + + return 0; +} + +/* Output data to a Kerberos encryption buffer. */ + +static int +krb_encrypt_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; + char inbuf[BUFFER_DATA_SIZE + 16]; + char outbuf[BUFFER_DATA_SIZE + 16]; + int aligned; + + if (have > BUFFER_DATA_SIZE) + { + /* It would be easy to malloc a buffer, but I don't think this + case can ever arise. */ + abort (); + } + + inbuf[0] = (have >> 8) & 0xff; + inbuf[1] = have & 0xff; + memcpy (inbuf + 2, data, have); + + /* For security against a known plaintext attack, we should + initialize any padding bytes to random values. Instead, we + just pick up whatever is on the stack, which is at least better + than using zero. */ + + /* Align (have + 2) (plus 2 for the count) to an 8 byte boundary. */ + aligned = (have + 2 + 7) & ~7; + + /* We use des_cbc_encrypt rather than krb_mk_priv because the + latter sticks a timestamp in the block, and krb_rd_priv expects + that timestamp to be within five minutes of the current time. + Given the way the CVS server buffers up data, that can easily + fail over a long network connection. We trust krb_recvauth to + guard against a replay attack. */ + + des_cbc_encrypt ((C_Block *) inbuf, (C_Block *) (outbuf + 2), aligned, + kb->sched, &kb->block, 1); + + outbuf[0] = (aligned >> 8) & 0xff; + outbuf[1] = aligned & 0xff; + + /* FIXME: It would be more efficient to get des_cbc_encrypt to put + its output directly into a buffer_data structure, which we + could then append to kb->buf. That would save a memcpy. */ + + buf_output (kb->buf, outbuf, aligned + 2); + + *wrote = have; + + /* We will only be here because buf_send_output was called on the + encryption buffer. That means that we should now call + buf_send_output on the underlying buffer. */ + return buf_send_output (kb->buf); +} + +/* Flush data to a Kerberos encryption buffer. */ + +static int +krb_encrypt_buffer_flush (closure) + void *closure; +{ + struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; + + /* Flush the underlying buffer. Note that if the original call to + buf_flush passed 1 for the BLOCK argument, then the buffer will + already have been set into blocking mode, so we should always + pass 0 here. */ + return buf_flush (kb->buf, 0); +} + +/* The block routine for a Kerberos encryption buffer. */ + +static int +krb_encrypt_buffer_block (closure, block) + void *closure; + int block; +{ + struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; + + if (block) + return set_block (kb->buf); + else + return set_nonblock (kb->buf); +} + +/* Shut down a Kerberos encryption buffer. */ + +static int +krb_encrypt_buffer_shutdown (closure) + void *closure; +{ + struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; + + return buf_shutdown (kb->buf); +} + +#endif /* HAVE_KERBEROS */ +#endif /* ENCRYPTION */ +#endif /* defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */ + /* Output LEN bytes at STR. If LEN is zero, then output up to (not including) - the first '\0' byte. Should not be called from the server parent process - (yet at least, in the future it might be extended so that works). */ + the first '\0' byte. */ void cvs_output (str, len) - char *str; + const char *str; size_t len; { if (len == 0) len = strlen (str); - if (error_use_protocol) - /* Eventually we'll probably want to make it so this case works, - but for now, callers who want to output something with - error_use_protocol in effect can just printf the "M foo" - themselves. */ - abort (); #ifdef SERVER_SUPPORT - if (server_active) + if (error_use_protocol) { - buf_output (&saved_output, str, len); - buf_copy_lines (&protocol, &saved_output, 'M'); - buf_send_counted (&protocol); + buf_output (saved_output, str, len); + buf_copy_lines (buf_to_net, saved_output, 'M'); + } + else if (server_active) + { + buf_output (saved_output, str, len); + buf_copy_lines (protocol, saved_output, 'M'); + buf_send_counted (protocol); } else #endif { size_t written; size_t to_write = len; - char *p = str; + const char *p = str; while (to_write > 0) { - written = fwrite (str, 1, to_write, stdout); + written = fwrite (p, 1, to_write, stdout); if (written == 0) break; p += written; @@ -4605,34 +5191,39 @@ cvs_output (str, len) void cvs_outerr (str, len) - char *str; + const char *str; size_t len; { if (len == 0) len = strlen (str); - if (error_use_protocol) - /* Eventually we'll probably want to make it so this case works, - but for now, callers who want to output something with - error_use_protocol in effect can just printf the "E foo" - themselves. */ - abort (); #ifdef SERVER_SUPPORT - if (server_active) + if (error_use_protocol) { - buf_output (&saved_outerr, str, len); - buf_copy_lines (&protocol, &saved_outerr, 'E'); - buf_send_counted (&protocol); + buf_output (saved_outerr, str, len); + buf_copy_lines (buf_to_net, saved_outerr, 'E'); + } + else if (server_active) + { + buf_output (saved_outerr, str, len); + buf_copy_lines (protocol, saved_outerr, 'E'); + buf_send_counted (protocol); } else #endif { size_t written; size_t to_write = len; - char *p = str; + const char *p = str; + + /* Make sure that output appears in order if stdout and stderr + point to the same place. For the server case this is taken + care of by the fact that saved_outerr always holds less + than a line. */ + fflush (stdout); while (to_write > 0) { - written = fwrite (str, 1, to_write, stderr); + written = fwrite (p, 1, to_write, stderr); if (written == 0) break; p += written; @@ -4640,3 +5231,52 @@ cvs_outerr (str, len) } } } + +/* Flush stderr. stderr is normally flushed automatically, of course, + but this function is used to flush information from the server back + to the client. */ + +void +cvs_flusherr () +{ +#ifdef SERVER_SUPPORT + if (error_use_protocol) + { + /* Flush what we can to the network, but don't block. */ + buf_flush (buf_to_net, 0); + } + else if (server_active) + { + /* Send a special count to tell the parent to flush. */ + buf_send_special_count (protocol, -1); + } + else +#endif + fflush (stderr); +} + +/* Make it possible for the user to see what has been written to + stdout (it is up to the implementation to decide exactly how far it + should go to ensure this). */ + +void +cvs_flushout () +{ +#ifdef SERVER_SUPPORT + if (error_use_protocol) + { + /* Flush what we can to the network, but don't block. */ + buf_flush (buf_to_net, 0); + } + else if (server_active) + { + /* Just do nothing. This is because the code which + cvs_flushout replaces, setting stdout to line buffering in + main.c, didn't get called in the server child process. But + in the future it is quite plausible that we'll want to make + this case work analogously to cvs_flusherr. */ + } + else +#endif + fflush (stdout); +} diff --git a/contrib/cvs/src/server.h b/contrib/cvs/src/server.h index 30ddb8c..cbda7a5 100644 --- a/contrib/cvs/src/server.h +++ b/contrib/cvs/src/server.h @@ -21,6 +21,9 @@ extern int server_expanding; /* Run the server. */ extern int server PROTO((int argc, char **argv)); +/* See server.c for description. */ +extern void server_pathname_check PROTO ((char *)); + /* We have a new Entries line for a file. TAG or DATE can be NULL. */ extern void server_register PROTO((char *name, char *version, char *timestamp, @@ -49,19 +52,30 @@ extern void server_checked_in extern void server_copy_file PROTO((char *file, char *update_dir, char *repository, char *newfile)); -/* - * We just successfully updated FILE (bare filename, no directory). - * REPOSITORY is the directory for the repository. This is called - * after server_register or server_scratch, in the latter case the - * file is to be removed. UPDATED indicates whether the file is now - * up to date (SERVER_UPDATED, yes, SERVER_MERGED, no, SERVER_PATCHED, - * yes, but file is a diff from user version to repository version). - */ -enum server_updated_arg4 {SERVER_UPDATED, SERVER_MERGED, SERVER_PATCHED}; +/* Send the appropriate responses for a file described by FILE, + UPDATE_DIR, REPOSITORY, and VERS. FILE_INFO is the result of + statting the file, or NULL if it hasn't been statted yet. This is + called after server_register or server_scratch. In the latter case + the file is to be removed (and vers can be NULL). In the former + case, vers must be non-NULL, and UPDATED indicates whether the file + is now up to date (SERVER_UPDATED, yes, SERVER_MERGED, no, + SERVER_PATCHED, yes, but file is a diff from user version to + repository version, SERVER_RCS_DIFF, yes, like SERVER_PATCHED but + with an RCS style diff). */ +enum server_updated_arg4 +{ + SERVER_UPDATED, + SERVER_MERGED, + SERVER_PATCHED, + SERVER_RCS_DIFF +}; extern void server_updated - PROTO((char *file, char *update_dir, char *repository, - enum server_updated_arg4 updated, struct stat *, - unsigned char *checksum)); + PROTO((struct file_info *finfo, Vers_TS *vers, + enum server_updated_arg4 updated, struct stat *, + unsigned char *checksum)); + +/* Whether we should send RCS format patches. */ +extern int server_use_rcs_diff PROTO((void)); /* Set the Entries.Static flag. */ extern void server_set_entstat PROTO((char *update_dir, char *repository)); @@ -70,14 +84,18 @@ extern void server_clear_entstat PROTO((char *update_dir, char *repository)); /* Set or clear a per-directory sticky tag or date. */ extern void server_set_sticky PROTO((char *update_dir, char *repository, - char *tag, - char *date)); + char *tag, char *date, int nonbranch)); /* Send Template response. */ extern void server_template PROTO ((char *, char *)); extern void server_update_entries PROTO((char *file, char *update_dir, char *repository, - enum server_updated_arg4 updated)); + enum server_updated_arg4 updated)); + +/* Pointer to a malloc'd string which is the directory which + the server should prepend to the pathnames which it sends + to the client. */ +extern char *server_dir; enum progs {PROG_CHECKIN, PROG_UPDATE}; extern void server_prog PROTO((char *, char *, enum progs)); @@ -134,5 +152,3 @@ struct request /* Table of requests ending with an entry with a NULL name. */ extern struct request requests[]; - -extern int use_unchanged; diff --git a/contrib/cvs/src/status.c b/contrib/cvs/src/status.c index 277da0c..67b71be 100644 --- a/contrib/cvs/src/status.c +++ b/contrib/cvs/src/status.c @@ -10,8 +10,10 @@ #include "cvs.h" -static Dtype status_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int status_fileproc PROTO((struct file_info *finfo)); +static Dtype status_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int status_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int tag_list_proc PROTO((Node * p, void *closure)); static int local = 0; @@ -39,7 +41,7 @@ status (argc, argv) usage (status_usage); optind = 1; - while ((c = getopt (argc, argv, "vlR")) != -1) + while ((c = getopt (argc, argv, "+vlR")) != -1) { switch (c) { @@ -75,9 +77,13 @@ status (argc, argv) send_arg("-l"); send_file_names (argc, argv, SEND_EXPAND_WILD); - /* XXX This should only need to send file info; the file - contents themselves will not be examined. */ - send_files (argc, argv, local, 0); + /* Note that by setting SEND_NO_CONTENTS, we do prevent the + server from updating our timestamp if the timestamp is + unchanged and the file is unmodified. And I think it is a + user-visible thing in that case (shows "locally modified" + instead of "up to date" I would think). But the speed seems + to be worth it. */ + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); send_to_server ("status\012", 0); err = get_responses_and_close (); @@ -87,9 +93,10 @@ status (argc, argv) #endif /* start the recursion processor */ - err = start_recursion (status_fileproc, (FILESDONEPROC) NULL, status_dirproc, - (DIRLEAVEPROC) NULL, argc, argv, local, - W_LOCAL, 0, 1, (char *) NULL, 1, 0); + err = start_recursion (status_fileproc, (FILESDONEPROC) NULL, + status_dirproc, (DIRLEAVEPROC) NULL, NULL, + argc, argv, local, + W_LOCAL, 0, 1, (char *) NULL, 1); return (err); } @@ -99,16 +106,17 @@ status (argc, argv) */ /* ARGSUSED */ static int -status_fileproc (finfo) +status_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { Ctype status; char *sstat; Vers_TS *vers; - status = Classify_File (finfo->file, (char *) NULL, (char *) NULL, (char *) NULL, - 1, 0, finfo->repository, finfo->entries, finfo->rcs, &vers, - finfo->update_dir, 0); + status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL, + 1, 0, &vers, 0); + sstat = "Classify Error"; switch (status) { case T_UNKNOWN: @@ -123,6 +131,9 @@ status_fileproc (finfo) break; #endif case T_CONFLICT: + /* I _think_ that "unresolved" is correct; that if it has + been resolved then the status will change. But I'm not + sure about that. */ sstat = "Unresolved Conflict"; break; case T_ADDED: @@ -133,7 +144,7 @@ status_fileproc (finfo) break; case T_MODIFIED: if (vers->ts_conflict) - sstat = "Unresolved Conflict"; + sstat = "File had conflicts on merge"; else sstat = "Locally Modified"; break; @@ -146,34 +157,66 @@ status_fileproc (finfo) case T_NEEDS_MERGE: sstat = "Needs Merge"; break; - default: - sstat = "Classify Error"; + case T_TITLE: + /* I don't think this case can occur here. Just print + "Classify Error". */ break; } - (void) printf ("===================================================================\n"); + cvs_output ("\ +===================================================================\n", 0); if (vers->ts_user == NULL) - (void) printf ("File: no file %s\t\tStatus: %s\n\n", finfo->file, sstat); + { + cvs_output ("File: no file ", 0); + cvs_output (finfo->file, 0); + cvs_output ("\t\tStatus: ", 0); + cvs_output (sstat, 0); + cvs_output ("\n\n", 0); + } else - (void) printf ("File: %-17s\tStatus: %s\n\n", finfo->file, sstat); + { + char *buf; + buf = xmalloc (strlen (finfo->file) + strlen (sstat) + 80); + sprintf (buf, "File: %-17s\tStatus: %s\n\n", finfo->file, sstat); + cvs_output (buf, 0); + free (buf); + } if (vers->vn_user == NULL) - (void) printf (" Working revision:\tNo entry for %s\n", finfo->file); + { + cvs_output (" Working revision:\tNo entry for ", 0); + cvs_output (finfo->file, 0); + cvs_output ("\n", 0); + } else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') - (void) printf (" Working revision:\tNew file!\n"); + cvs_output (" Working revision:\tNew file!\n", 0); #ifdef SERVER_SUPPORT else if (server_active) - (void) printf (" Working revision:\t%s\n", vers->vn_user); + { + cvs_output (" Working revision:\t", 0); + cvs_output (vers->vn_user, 0); + cvs_output ("\n", 0); + } #endif else - (void) printf (" Working revision:\t%s\t%s\n", vers->vn_user, - vers->ts_rcs); + { + cvs_output (" Working revision:\t", 0); + cvs_output (vers->vn_user, 0); + cvs_output ("\t", 0); + cvs_output (vers->ts_rcs, 0); + cvs_output ("\n", 0); + } if (vers->vn_rcs == NULL) - (void) printf (" Repository revision:\tNo revision control file\n"); + cvs_output (" Repository revision:\tNo revision control file\n", 0); else - (void) printf (" Repository revision:\t%s\t%s\n", vers->vn_rcs, - vers->srcfile->path); + { + cvs_output (" Repository revision:\t", 0); + cvs_output (vers->vn_rcs, 0); + cvs_output ("\t", 0); + cvs_output (vers->srcfile->path, 0); + cvs_output ("\n", 0); + } if (vers->entdata) { @@ -183,24 +226,33 @@ status_fileproc (finfo) if (edata->tag) { if (vers->vn_rcs == NULL) - (void) printf ( - " Sticky Tag:\t\t%s - MISSING from RCS file!\n", - edata->tag); + { + cvs_output (" Sticky Tag:\t\t", 0); + cvs_output (edata->tag, 0); + cvs_output (" - MISSING from RCS file!\n", 0); + } else { if (isdigit (edata->tag[0])) - (void) printf (" Sticky Tag:\t\t%s\n", edata->tag); + { + cvs_output (" Sticky Tag:\t\t", 0); + cvs_output (edata->tag, 0); + cvs_output ("\n", 0); + } else { char *branch = NULL; - + if (RCS_isbranch (finfo->rcs, edata->tag)) branch = RCS_whatbranch(finfo->rcs, edata->tag); - (void) printf (" Sticky Tag:\t\t%s (%s: %s)\n", - edata->tag, - branch ? "branch" : "revision", - branch ? branch : vers->vn_rcs); + cvs_output (" Sticky Tag:\t\t", 0); + cvs_output (edata->tag, 0); + cvs_output (" (", 0); + cvs_output (branch ? "branch" : "revision", 0); + cvs_output (": ", 0); + cvs_output (branch ? branch : vers->vn_rcs, 0); + cvs_output (")\n", 0); if (branch) free (branch); @@ -208,34 +260,42 @@ status_fileproc (finfo) } } else if (!really_quiet) - (void) printf (" Sticky Tag:\t\t(none)\n"); + cvs_output (" Sticky Tag:\t\t(none)\n", 0); if (edata->date) - (void) printf (" Sticky Date:\t\t%s\n", edata->date); + { + cvs_output (" Sticky Date:\t\t", 0); + cvs_output (edata->date, 0); + cvs_output ("\n", 0); + } else if (!really_quiet) - (void) printf (" Sticky Date:\t\t(none)\n"); + cvs_output (" Sticky Date:\t\t(none)\n", 0); if (edata->options && edata->options[0]) - (void) printf (" Sticky Options:\t%s\n", edata->options); + { + cvs_output (" Sticky Options:\t", 0); + cvs_output (edata->options, 0); + cvs_output ("\n", 0); + } else if (!really_quiet) - (void) printf (" Sticky Options:\t(none)\n"); + cvs_output (" Sticky Options:\t(none)\n", 0); if (long_format && vers->srcfile) { List *symbols = RCS_symbols(vers->srcfile); - (void) printf ("\n Existing Tags:\n"); + cvs_output ("\n Existing Tags:\n", 0); if (symbols) { xrcsnode = finfo->rcs; (void) walklist (symbols, tag_list_proc, NULL); } else - (void) printf ("\tNo Tags Exist\n"); + cvs_output ("\tNo Tags Exist\n", 0); } } - (void) printf ("\n"); + cvs_output ("\n", 0); freevers_ts (&vers); return (0); } @@ -245,10 +305,12 @@ status_fileproc (finfo) */ /* ARGSUSED */ static Dtype -status_dirproc (dir, repos, update_dir) +status_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "Examining %s", update_dir); @@ -264,13 +326,18 @@ tag_list_proc (p, closure) void *closure; { char *branch = NULL; + char *buf; if (RCS_isbranch (xrcsnode, p->key)) branch = RCS_whatbranch(xrcsnode, p->key) ; - (void) printf ("\t%-25.25s\t(%s: %s)\n", p->key, - branch ? "branch" : "revision", - branch ? branch : p->data); + buf = xmalloc (80 + strlen (p->key) + + (branch ? strlen (branch) : strlen (p->data))); + sprintf (buf, "\t%-25s\t(%s: %s)\n", p->key, + branch ? "branch" : "revision", + branch ? branch : p->data); + cvs_output (buf, 0); + free (buf); if (branch) free (branch); diff --git a/contrib/cvs/src/subr.c b/contrib/cvs/src/subr.c index 8ed9177..6a88849 100644 --- a/contrib/cvs/src/subr.c +++ b/contrib/cvs/src/subr.c @@ -9,6 +9,7 @@ */ #include "cvs.h" +#include "getline.h" extern char *getlogin (); @@ -55,6 +56,43 @@ xrealloc (ptr, bytes) return (cp); } +/* Two constants which tune expand_string. Having MIN_INCR as large + as 1024 might waste a bit of memory, but it shouldn't be too bad + (CVS used to allocate arrays of, say, 3000, PATH_MAX (8192, often), + or other such sizes). Probably anything which is going to allocate + memory which is likely to get as big as MAX_INCR shouldn't be doing + it in one block which must be contiguous, but since getrcskey does + so, we might as well limit the wasted memory to MAX_INCR or so + bytes. */ + +#define MIN_INCR 1024 +#define MAX_INCR (2*1024*1024) + +/* *STRPTR is a pointer returned from malloc (or NULL), pointing to *N + characters of space. Reallocate it so that points to at least + NEWSIZE bytes of space. Gives a fatal error if out of memory; + if it returns it was successful. */ +void +expand_string (strptr, n, newsize) + char **strptr; + size_t *n; + size_t newsize; +{ + if (*n < newsize) + { + while (*n < newsize) + { + if (*n < MIN_INCR) + *n += MIN_INCR; + else if (*n > MAX_INCR) + *n += MAX_INCR; + else + *n *= 2; + } + *strptr = xrealloc (*strptr, *n); + } +} + /* * Duplicate a string, calling xmalloc to allocate some dynamic space */ @@ -76,15 +114,57 @@ void strip_trailing_newlines (str) char *str; { - int len; - len = strlen (str) - 1; + int len; + len = strlen (str) - 1; - while (str[len] == '\n') - str[len--] = '\0'; + while (str[len] == '\n') + str[len--] = '\0'; } +/* Return the number of levels that path ascends above where it starts. + For example: + "../../foo" -> 2 + "foo/../../bar" -> 1 + */ +/* FIXME: Should be using ISDIRSEP, last_component, or some other + mechanism which is more general than just looking at slashes, + particularly for the client.c caller. The server.c caller might + want something different, so be careful. */ +int +pathname_levels (path) + char *path; +{ + char *p; + char *q; + int level; + int max_level; + + max_level = 0; + p = path; + level = 0; + do + { + q = strchr (p, '/'); + if (q != NULL) + ++q; + if (p[0] == '.' && p[1] == '.' && (p[2] == '\0' || p[2] == '/')) + { + --level; + if (-level > max_level) + max_level = -level; + } + else if (p[0] == '.' && (p[1] == '\0' || p[1] == '/')) + ; + else + ++level; + p = q; + } while (p != NULL); + return max_level; +} + + /* - * Recover the space allocated by Find_Names() and line2argv() + * Recover the space allocated by line2argv() */ void free_names (pargc, argv) @@ -97,26 +177,42 @@ free_names (pargc, argv) { /* only do through *pargc */ free (argv[i]); } + free (argv); *pargc = 0; /* and set it to zero when done */ } -/* - * Convert a line into argc/argv components and return the result in the - * arguments as passed. Use free_names() to return the memory allocated here - * back to the free pool. - */ +/* Convert LINE into arguments separated by space and tab. Set *ARGC + to the number of arguments found, and (*ARGV)[0] to the first argument, + (*ARGV)[1] to the second, etc. *ARGV is malloc'd and so are each of + (*ARGV)[0], (*ARGV)[1], ... Use free_names() to return the memory + allocated here back to the free pool. */ void line2argv (pargc, argv, line) int *pargc; - char **argv; + char ***argv; char *line; { char *cp; + /* Could make a case for size_t or some other unsigned type, but + we'll stick with int to avoid signed/unsigned warnings when + comparing with *pargc. */ + int argv_allocated; + + /* Small for testing. */ + /* argv_allocated must be at least 3 because at some places + (e.g. checkout_proc) cvs alters argv[2]. */ + argv_allocated = 4; + *argv = (char **) xmalloc (argv_allocated * sizeof (**argv)); *pargc = 0; for (cp = strtok (line, " \t"); cp; cp = strtok ((char *) NULL, " \t")) { - argv[*pargc] = xstrdup (cp); + if (*pargc == argv_allocated) + { + argv_allocated *= 2; + *argv = xrealloc (*argv, argv_allocated * sizeof (**argv)); + } + (*argv)[*pargc] = xstrdup (cp); (*pargc)++; } } @@ -192,9 +288,10 @@ gca (rev1, rev2) char *rev2; { int dots; - char gca[PATH_MAX]; + char *gca; char *p[2]; int j[2]; + char *retval; if (rev1 == NULL || rev2 == NULL) { @@ -202,6 +299,11 @@ gca (rev1, rev2) abort(); } + /* The greatest common ancestor will have no more dots, and numbers + of digits for each component no greater than the arguments. Therefore + this string will be big enough. */ + gca = xmalloc (strlen (rev1) + strlen (rev2) + 100); + /* walk the strings, reading the common parts. */ gca[0] = '\0'; p[0] = rev1; @@ -289,7 +391,9 @@ gca (rev1, rev2) *s = '\0'; } - return (xstrdup (gca)); + retval = xstrdup (gca); + free (gca); + return retval; } /* @@ -316,3 +420,39 @@ make_message_rcslegal (message) return message; } + +/* Does the file FINFO contain conflict markers? The whole concept + of looking at the contents of the file to figure out whether there are + unresolved conflicts is kind of bogus (people do want to manage files + which contain those patterns not as conflict markers), but for now it + is what we do. */ +int +file_has_markers (finfo) + struct file_info *finfo; +{ + FILE *fp; + char *line = NULL; + size_t line_allocated = 0; + int result; + + result = 0; + fp = CVS_FOPEN (finfo->file, "r"); + if (fp == NULL) + error (1, errno, "cannot open %s", finfo->fullname); + while (getline (&line, &line_allocated, fp) > 0) + { + if (strncmp (line, RCS_MERGE_PAT, sizeof RCS_MERGE_PAT - 1) == 0) + { + result = 1; + goto out; + } + } + if (ferror (fp)) + error (0, errno, "cannot read %s", finfo->fullname); +out: + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", finfo->fullname); + if (line != NULL) + free (line); + return result; +} diff --git a/contrib/cvs/src/tag.c b/contrib/cvs/src/tag.c index 2e30009..9c49da5 100644 --- a/contrib/cvs/src/tag.c +++ b/contrib/cvs/src/tag.c @@ -14,15 +14,22 @@ #include "cvs.h" #include "savecwd.h" -static int check_fileproc PROTO((struct file_info *finfo)); -static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); +static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int check_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static int pretag_proc PROTO((char *repository, char *filter)); static void masterlist_delproc PROTO((Node *p)); static void tag_delproc PROTO((Node *p)); static int pretag_list_proc PROTO((Node *p, void *closure)); -static Dtype tag_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int tag_fileproc PROTO((struct file_info *finfo)); +static Dtype tag_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int tag_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static char *numtag; static char *date = NULL; @@ -32,6 +39,7 @@ static int branch_mode; /* make an automagic "branch" tag */ static int local; /* recursive by default */ static int force_tag_match = 1; /* force tag to match by default */ static int force_tag_move; /* don't force tag to move by default */ +static int check_uptodate; /* no uptodate-check by default */ struct tag_info { @@ -51,19 +59,20 @@ static List *tlist; static const char *const tag_usage[] = { - "Usage: %s %s [-lRF] [-b] [-d] [-r tag|-D date] tag [files...]\n", + "Usage: %s %s [-lRF] [-b] [-d] [-c] [-r tag|-D date] tag [files...]\n", "\t-l\tLocal directory only, not recursive.\n", "\t-R\tProcess directories recursively.\n", - "\t-d\tDelete the given Tag.\n", + "\t-d\tDelete the given tag.\n", "\t-[rD]\tExisting tag or date.\n", - "\t-f\tForce a head revision if tag etc not found.\n", + "\t-f\tForce a head revision if specified tag not found.\n", "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", - "\t-F\tMove tag if it already exists\n", + "\t-F\tMove tag if it already exists.\n", + "\t-c\tCheck that working files are unmodified.\n", NULL }; int -tag (argc, argv) +cvstag (argc, argv) int argc; char **argv; { @@ -74,7 +83,7 @@ tag (argc, argv) usage (tag_usage); optind = 1; - while ((c = getopt (argc, argv, "FQqlRdr:D:bf")) != -1) + while ((c = getopt (argc, argv, "+FQqlRcdr:D:bf")) != -1) { switch (c) { @@ -98,6 +107,9 @@ tag (argc, argv) case 'd': delete_flag = 1; break; + case 'c': + check_uptodate = 1; + break; case 'r': numtag = optarg; break; @@ -144,10 +156,14 @@ tag (argc, argv) ign_setup (); + if (!force_tag_match) + send_arg ("-f"); if (local) send_arg("-l"); if (delete_flag) send_arg("-d"); + if (check_uptodate) + send_arg("-c"); if (branch_mode) send_arg("-b"); if (force_tag_move) @@ -161,10 +177,14 @@ tag (argc, argv) send_arg (symtag); send_file_names (argc, argv, SEND_EXPAND_WILD); - /* FIXME: We shouldn't have to send current files, but I'm not sure - whether it works. So send the files -- - it's slower but it works. */ - send_files (argc, argv, local, 0); + + /* SEND_NO_CONTENTS has a mildly bizarre interaction with + check_uptodate; if the timestamp is modified but the file + is unmodified, the check will fail, only to have "cvs diff" + show no differences (and one must do "update" or something to + reset the client's notion of the timestamp). */ + + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); send_to_server ("tag\012", 0); return get_responses_and_close (); } @@ -178,9 +198,9 @@ tag (argc, argv) mtlist = getlist(); err = start_recursion (check_fileproc, check_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 1, - (char *) NULL, 1, 0); + (char *) NULL, 1); if (err) { @@ -188,9 +208,9 @@ tag (argc, argv) } /* start the recursion processor */ - err = start_recursion (tag_fileproc, (FILESDONEPROC) NULL, tag_dirproc, - (DIRLEAVEPROC) NULL, argc, argv, local, - W_LOCAL, 0, 1, (char *) NULL, 1, 0); + err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc, + (DIRLEAVEPROC) NULL, NULL, argc, argv, local, + W_LOCAL, 0, 0, (char *) NULL, 1); dellist(&mtlist); return (err); } @@ -199,13 +219,25 @@ tag (argc, argv) /* All we do here is add it to our list */ static int -check_fileproc (finfo) +check_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *xdir; Node *p; Vers_TS *vers; + if (check_uptodate) + { + Ctype status = Classify_File (finfo, (char *) NULL, (char *) NULL, + (char *) NULL, 1, 0, &vers, 0); + if ((status != T_UPTODATE) && (status != T_CHECKOUT)) + { + error (0, 0, "%s is locally modified", finfo->fullname); + return (1); + } + } + if (finfo->update_dir[0] == '\0') xdir = "."; else @@ -234,22 +266,22 @@ check_fileproc (finfo) p->key = xstrdup (finfo->file); p->type = UPDATE; p->delproc = tag_delproc; - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, - (char *) NULL, finfo->file, 0, 0, - finfo->entries, finfo->rcs); + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); if (vers->srcfile == NULL) { if (!really_quiet) error (0, 0, "nothing known about %s", finfo->file); return (1); } - p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0); + p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, + (int *) NULL); if (p->data != NULL) { int addit = 1; char *oversion; - oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); if (oversion == NULL) { if (delete_flag) @@ -281,10 +313,12 @@ check_fileproc (finfo) } static int -check_filesdoneproc(err, repos, update_dir) +check_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; int err; char *repos; char *update_dir; + List *entries; { int n; Node *p; @@ -389,7 +423,8 @@ pretag_list_proc(p, closure) */ /* ARGSUSED */ static int -tag_fileproc (finfo) +tag_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *version, *oversion; @@ -398,15 +433,26 @@ tag_fileproc (finfo) Vers_TS *vers; int retcode = 0; - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL, - finfo->file, 0, 0, finfo->entries, finfo->rcs); + /* Lock the directory if it is not already locked. We can't rely + on tag_dirproc because it won't handle the case where the user + specifies a list of files on the command line. */ + /* We do not need to acquire a full write lock for the tag operation: + the revisions are obtained from the working directory, so we do not + require consistency across the entire repository. However, we do + need to prevent simultaneous tag operations from interfering with + each other. Therefore, we write lock each directory as we enter + it, and unlock it as we leave it. */ + lock_dir_for_write (finfo->repository); + + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); if ((numtag != NULL) || (date != NULL)) { nversion = RCS_getversion(vers->srcfile, numtag, date, - force_tag_match, 0); + force_tag_match, + (int *) NULL); if (nversion == NULL) { freevers_ts (&vers); @@ -425,7 +471,8 @@ tag_fileproc (finfo) * "rcs" to remove the tag... trust me. */ - version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); if (version == NULL || vers->srcfile == NULL) { freevers_ts (&vers); @@ -433,7 +480,7 @@ tag_fileproc (finfo) } free (version); - if ((retcode = RCS_deltag(vers->srcfile->path, symtag, 1)) != 0) + if ((retcode = RCS_deltag(vers->srcfile, symtag, 1)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -446,7 +493,9 @@ tag_fileproc (finfo) /* warm fuzzies */ if (!really_quiet) { - (void) printf ("D %s\n", finfo->fullname); + cvs_output ("D ", 2); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); } freevers_ts (&vers); @@ -501,7 +550,8 @@ tag_fileproc (finfo) * module -- which I have found to be a typical tagging operation. */ rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; - oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); if (oversion != NULL) { int isbranch = RCS_isbranch (finfo->rcs, symtag); @@ -520,12 +570,19 @@ tag_fileproc (finfo) if (!force_tag_move) { /* we're NOT going to move the tag */ - (void) printf ("W %s", finfo->fullname); - - (void) printf (" : %s already exists on %s %s", - symtag, isbranch ? "branch" : "version", oversion); - (void) printf (" : NOT MOVING tag to %s %s\n", - branch_mode ? "branch" : "version", rev); + cvs_output ("W ", 2); + cvs_output (finfo->fullname, 0); + cvs_output (" : ", 0); + cvs_output (symtag, 0); + cvs_output (" already exists on ", 0); + cvs_output (isbranch ? "branch" : "version", 0); + cvs_output (" ", 0); + cvs_output (oversion, 0); + cvs_output (" : NOT MOVING tag to ", 0); + cvs_output (branch_mode ? "branch" : "version", 0); + cvs_output (" ", 0); + cvs_output (rev, 0); + cvs_output ("\n", 1); free (oversion); freevers_ts (&vers); return (0); @@ -533,7 +590,7 @@ tag_fileproc (finfo) free (oversion); } - if ((retcode = RCS_settag(vers->srcfile->path, symtag, rev)) != 0) + if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0) { error (1, retcode == -1 ? errno : 0, "failed to set tag %s to revision %s in %s", @@ -545,7 +602,9 @@ tag_fileproc (finfo) /* more warm fuzzies */ if (!really_quiet) { - (void) printf ("T %s\n", finfo->fullname); + cvs_output ("T ", 2); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); } if (nversion != NULL) @@ -556,15 +615,32 @@ tag_fileproc (finfo) return (0); } +/* Clear any lock we may hold on the current directory. */ + +static int +tag_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; + int err; + char *repos; + char *update_dir; + List *entries; +{ + Lock_Cleanup (); + + return (err); +} + /* * Print a warm fuzzy message */ /* ARGSUSED */ static Dtype -tag_dirproc (dir, repos, update_dir) +tag_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); @@ -584,18 +660,15 @@ struct val_args { int found; }; -/* Pass as a static until we get around to fixing start_recursion to pass along - a void * where we can stash it. */ -static struct val_args *val_args_static; - -static int val_fileproc PROTO ((struct file_info *finfo)); +static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -val_fileproc (finfo) +val_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { RCSNode *rcsdata; - struct val_args *args = val_args_static; + struct val_args *args = (struct val_args *)callerdat; char *tag; if ((rcsdata = finfo->rcs) == NULL) @@ -603,7 +676,7 @@ val_fileproc (finfo) W_REPOS | W_ATTIC. */ return 0; - tag = RCS_gettag (rcsdata, args->name, 1, 0); + tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL); if (tag != NULL) { /* FIXME: should find out a way to stop the search at this point. */ @@ -613,13 +686,15 @@ val_fileproc (finfo) return 0; } -static Dtype val_direntproc PROTO ((char *, char *, char *)); +static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *)); static Dtype -val_direntproc (dir, repository, update_dir) +val_direntproc (callerdat, dir, repository, update_dir, entries) + void *callerdat; char *dir; char *repository; char *update_dir; + List *entries; { /* This is not quite right--it doesn't get right the case of "cvs update -d -r foobar" where foobar is a tag which exists only in @@ -672,12 +747,18 @@ Numeric tag %s contains characters other than digits and '.'", name); return; } + /* Special tags are always valid. */ + if (strcmp (name, TAG_BASE) == 0 + || strcmp (name, TAG_HEAD) == 0) + return; + mytag.dptr = name; mytag.dsize = strlen (name); - valtags_filename = xmalloc (strlen (CVSroot) + sizeof CVSROOTADM - + sizeof CVSROOTADM_HISTORY + 20); - strcpy (valtags_filename, CVSroot); + valtags_filename = xmalloc (strlen (CVSroot_directory) + + sizeof CVSROOTADM + + sizeof CVSROOTADM_VALTAGS + 20); + strcpy (valtags_filename, CVSroot_directory); strcat (valtags_filename, "/"); strcat (valtags_filename, CVSROOTADM); strcat (valtags_filename, "/"); @@ -709,11 +790,15 @@ Numeric tag %s contains characters other than digits and '.'", name); /* We didn't find the tag in val-tags, so look through all the RCS files to see whether it exists there. Yes, this is expensive, but there is no other way to cope with a tag which might have been created - by an old version of CVS, from before val-tags was invented. */ + by an old version of CVS, from before val-tags was invented. + + Since we need this code anyway, we also use it to create + entries in val-tags in general (that is, the val-tags entry + will get created the first time the tag is used, not when the + tag is created). */ the_val_args.name = name; the_val_args.found = 0; - val_args_static = &the_val_args; which = W_REPOS | W_ATTIC; @@ -724,16 +809,17 @@ Numeric tag %s contains characters other than digits and '.'", name); else { if (save_cwd (&cwd)) - exit (EXIT_FAILURE); - if (chdir (repository) < 0) + error_exit (); + if ( CVS_CHDIR (repository) < 0) error (1, errno, "cannot change to %s directory", repository); } } err = start_recursion (val_fileproc, (FILESDONEPROC) NULL, val_direntproc, (DIRLEAVEPROC) NULL, + (void *)&the_val_args, argc, argv, local, which, aflag, - 1, NULL, 1, 0); + 1, NULL, 1); if (repository != NULL && repository[0] != '\0') { if (restore_cwd (&cwd, NULL)) @@ -779,3 +865,36 @@ Numeric tag %s contains characters other than digits and '.'", name); } free (valtags_filename); } + +/* + * Check whether a join tag is valid. This is just like + * tag_check_valid, but we must stop before the colon if there is one. + */ + +void +tag_check_valid_join (join_tag, argc, argv, local, aflag, repository) + char *join_tag; + int argc; + char **argv; + int local; + int aflag; + char *repository; +{ + char *c, *s; + + c = xstrdup (join_tag); + s = strchr (c, ':'); + if (s != NULL) + { + if (isdigit (join_tag[0])) + error (1, 0, + "Numeric join tag %s may not contain a date specifier", + join_tag); + + *s = '\0'; + } + + tag_check_valid (c, argc, argv, local, aflag, repository); + + free (c); +} diff --git a/contrib/cvs/src/update.c b/contrib/cvs/src/update.c index 2478316..4b7022a 100644 --- a/contrib/cvs/src/update.c +++ b/contrib/cvs/src/update.c @@ -34,43 +34,55 @@ */ #include "cvs.h" +#include "savecwd.h" #ifdef SERVER_SUPPORT #include "md5.h" #endif #include "watch.h" #include "fileattr.h" #include "edit.h" +#include "getline.h" -static int checkout_file PROTO((char *file, char *repository, List *entries, - RCSNode *rcsnode, Vers_TS *vers_ts, char *update_dir)); +static int checkout_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts, + int adding)); #ifdef SERVER_SUPPORT -static int patch_file PROTO((char *file, char *repository, List *entries, - RCSNode*rcsnode, Vers_TS *vers_ts, char *update_dir, - int *docheckout, struct stat *file_info, - unsigned char *checksum)); +static int patch_file PROTO ((struct file_info *finfo, + Vers_TS *vers_ts, + int *docheckout, struct stat *file_info, + unsigned char *checksum)); +static void patch_file_write PROTO ((void *, const char *, size_t)); #endif -static int isemptydir PROTO((char *dir)); -static int merge_file PROTO((char *file, char *repository, List *entries, - Vers_TS *vers, char *update_dir)); -static int scratch_file PROTO((char *file, char *repository, List * entries, - char *update_dir)); -static Dtype update_dirent_proc PROTO((char *dir, char *repository, char *update_dir)); -static int update_dirleave_proc PROTO((char *dir, int err, char *update_dir)); -static int update_fileproc PROTO ((struct file_info *)); -static int update_filesdone_proc PROTO((int err, char *repository, - char *update_dir)); +static int merge_file PROTO ((struct file_info *finfo, Vers_TS *vers)); +static int scratch_file PROTO((struct file_info *finfo)); +static Dtype update_dirent_proc PROTO ((void *callerdat, char *dir, + char *repository, char *update_dir, + List *entries)); +static int update_dirleave_proc PROTO ((void *callerdat, char *dir, + int err, char *update_dir, + List *entries)); +static int update_fileproc PROTO ((void *callerdat, struct file_info *)); +static int update_filesdone_proc PROTO ((void *callerdat, int err, + char *repository, char *update_dir, + List *entries)); static int write_letter PROTO((char *file, int letter, char *update_dir)); #ifdef SERVER_SUPPORT -static void join_file PROTO((char *file, RCSNode *rcsnode, Vers_TS *vers_ts, - char *update_dir, List *entries, char *repository)); +static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts)); #else -static void join_file PROTO((char *file, RCSNode *rcsnode, Vers_TS *vers_ts, - char *update_dir, List *entries)); +static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts)); #endif static char *options = NULL; static char *tag = NULL; static char *date = NULL; +/* This is a bit of a kludge. We call WriteTag at the beginning + before we know whether nonbranch is set or not. And then at the + end, once we have the right value for nonbranch, we call WriteTag + again. I don't know whether the first call is necessary or not. + rewrite_tag is nonzero if we are going to have to make that second + call. */ +static int rewrite_tag; +static int nonbranch; + static char *join_rev1, *date_rev1; static char *join_rev2, *date_rev2; static int aflag = 0; @@ -80,6 +92,7 @@ static int update_prune_dirs = 0; static int pipeout = 0; #ifdef SERVER_SUPPORT static int patches = 0; +static int rcs_diff_patches = 0; #endif static List *ignlist = (List *) NULL; static time_t last_register_time; @@ -93,10 +106,10 @@ static const char *const update_usage[] = "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, no recursion.\n", "\t-R\tProcess directories recursively.\n", - "\t-p\tSend updates to standard output.\n", + "\t-p\tSend updates to standard output (avoids stickiness).\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", - "\t-r rev\tUpdate using specified revision/tag.\n", - "\t-D date\tSet date to update from.\n", + "\t-r rev\tUpdate using specified revision/tag (is sticky).\n", + "\t-D date\tSet date to update from (is sticky).\n", "\t-j rev\tMerge in changes made between current revision and rev.\n", "\t-I ign\tMore files to ignore (! to reset).\n", "\t-W spec\tWrappers specification line.\n", @@ -123,7 +136,7 @@ update (argc, argv) /* parse the args */ optind = 1; - while ((c = getopt (argc, argv, "ApPflRQqduk:r:D:j:I:W:")) != -1) + while ((c = getopt (argc, argv, "+ApPflRQqduk:r:D:j:I:W:")) != -1) { switch (c) { @@ -188,7 +201,10 @@ update (argc, argv) case 'u': #ifdef SERVER_SUPPORT if (server_active) + { patches = 1; + rcs_diff_patches = server_use_rcs_diff (); + } else #endif usage (update_usage); @@ -205,9 +221,12 @@ update (argc, argv) #ifdef CLIENT_SUPPORT if (client_active) { + int pass; + /* The first pass does the regular update. If we receive at least one patch which failed, we do a second pass and just fetch those files whose patches failed. */ + pass = 1; do { int status; @@ -228,37 +247,37 @@ update (argc, argv) send_arg("-P"); client_prune_dirs = update_prune_dirs; option_with_arg ("-r", tag); + if (options && options[0] != '\0') + send_arg (options); if (date) client_senddate (date); if (join_rev1) option_with_arg ("-j", join_rev1); if (join_rev2) option_with_arg ("-j", join_rev2); + wrap_send (); /* If the server supports the command "update-patches", that means that it knows how to handle the -u argument to update, which means to send patches instead of complete files. */ if (failed_patches == NULL) { - struct request *rq; - - for (rq = requests; rq->name != NULL; rq++) - { - if (strcmp (rq->name, "update-patches") == 0) - { - if (rq->status == rq_supported) - { - send_arg("-u"); - } - break; - } - } +#ifndef DONT_USE_PATCH + /* Systems which don't have the patch program ported to them + will want to define DONT_USE_PATCH; then CVS won't try to + invoke patch. */ + if (supported_request ("update-patches")) + send_arg ("-u"); +#endif } if (failed_patches == NULL) { send_file_names (argc, argv, SEND_EXPAND_WILD); - send_files (argc, argv, local, aflag); + /* If noexec, probably could be setting SEND_NO_CONTENTS. + Same caveats as for "cvs status" apply. */ + send_files (argc, argv, local, aflag, + update_build_dirs ? SEND_BUILD_DIRS : 0); } else { @@ -267,8 +286,8 @@ update (argc, argv) (void) printf ("%s client: refetching unpatchable files\n", program_name); - if (toplevel_wd[0] != '\0' - && chdir (toplevel_wd) < 0) + if (toplevel_wd != NULL + && CVS_CHDIR (toplevel_wd) < 0) { error (1, errno, "could not chdir to %s", toplevel_wd); } @@ -277,7 +296,7 @@ update (argc, argv) (void) unlink_file (failed_patches[i]); send_file_names (failed_patches_count, failed_patches, 0); send_files (failed_patches_count, failed_patches, local, - aflag); + aflag, update_build_dirs ? SEND_BUILD_DIRS : 0); } failed_patches = NULL; @@ -286,9 +305,27 @@ update (argc, argv) send_to_server ("update\012", 0); status = get_responses_and_close (); - if (status != 0) + + /* If there are any conflicts, the server will return a + non-zero exit status. If any patches failed, we still + want to run the update again. We use a pass count to + avoid an endless loop. */ + + /* Notes: (1) assuming that status != 0 implies a + potential conflict is the best we can cleanly do given + the current protocol. I suppose that trying to + re-fetch in cases where there was a more serious error + is probably more or less harmless, but it isn't really + ideal. (2) it would be nice to have a testsuite case for the + conflict-and-patch-failed case. */ + + if (status != 0 + && (failed_patches == NULL || pass > 1)) + { return status; + } + ++pass; } while (failed_patches != NULL); return 0; @@ -297,8 +334,10 @@ update (argc, argv) if (tag != NULL) tag_check_valid (tag, argc, argv, local, aflag, ""); - /* FIXME: We don't call tag_check_valid on join_rev1 and join_rev2 - yet (make sure to handle ':' correctly if we do, though). */ + if (join_rev1 != NULL) + tag_check_valid_join (join_rev1, argc, argv, local, aflag, ""); + if (join_rev2 != NULL) + tag_check_valid_join (join_rev2, argc, argv, local, aflag, ""); /* * If we are updating the entire directory (for real) and building dirs @@ -320,11 +359,10 @@ update (argc, argv) /* keep the CVS/Tag file current with the specified arguments */ if (aflag || tag || date) { - WriteTag ((char *) NULL, tag, date); -#ifdef SERVER_SUPPORT - if (server_active) - server_set_sticky (".", Name_Repository (NULL, NULL), tag, date); -#endif + WriteTag ((char *) NULL, tag, date, 0, + ".", Name_Repository (NULL, NULL)); + rewrite_tag = 1; + nonbranch = 0; } } @@ -402,9 +440,9 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, /* call the recursion processor */ err = start_recursion (update_fileproc, update_filesdone_proc, - update_dirent_proc, update_dirleave_proc, + update_dirent_proc, update_dirleave_proc, NULL, argc, argv, local, which, aflag, 1, - preload_update_dir, 1, 0); + preload_update_dir, 1); /* see if we need to sleep before returning */ if (last_register_time) @@ -431,16 +469,36 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, * appropriate magic for checkout */ static int -update_fileproc (finfo) +update_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { int retval; Ctype status; Vers_TS *vers; + int resurrecting; + + resurrecting = 0; + + status = Classify_File (finfo, tag, date, options, force_tag_match, + aflag, &vers, pipeout); + + /* Keep track of whether TAG is a branch tag. + Note that if it is a branch tag in some files and a nonbranch tag + in others, treat it as a nonbranch tag. It is possible that case + should elicit a warning or an error. */ + if (rewrite_tag + && tag != NULL + && finfo->rcs != NULL) + { + char *rev = RCS_getversion (finfo->rcs, tag, NULL, 1, NULL); + if (rev != NULL + && !RCS_nodeisbranch (finfo->rcs, tag)) + nonbranch = 1; + if (rev != NULL) + free (rev); + } - status = Classify_File (finfo->file, tag, date, options, force_tag_match, - aflag, finfo->repository, finfo->entries, finfo->rcs, &vers, - finfo->update_dir, pipeout); if (pipeout) { /* @@ -469,8 +527,7 @@ update_fileproc (finfo) #ifdef SERVER_SUPPORT case T_PATCH: /* needs patch */ #endif - retval = checkout_file (finfo->file, finfo->repository, finfo->entries, finfo->rcs, - vers, finfo->update_dir); + retval = checkout_file (finfo, vers, 0); break; default: /* can't ever happen :-) */ @@ -504,11 +561,10 @@ update_fileproc (finfo) if (wrap_merge_is_copy (finfo->file)) /* Should we be warning the user that we are * overwriting the user's copy of the file? */ - retval = checkout_file (finfo->file, finfo->repository, finfo->entries, - finfo->rcs, vers, finfo->update_dir); + retval = + checkout_file (finfo, vers, 0); else - retval = merge_file (finfo->file, finfo->repository, finfo->entries, - vers, finfo->update_dir); + retval = merge_file (finfo, vers); } break; case T_MODIFIED: /* locally modified */ @@ -538,22 +594,11 @@ update_fileproc (finfo) if (retcode) { - /* - * If the timestamps differ, look for Conflict - * indicators to see if 'C' anyway. - */ - run_setup ("%s", GREP); - run_arg (RCS_MERGE_PAT); - run_arg (finfo->file); - retcode = run_exec (RUN_TTY, DEVNULL, - RUN_TTY,RUN_NORMAL); - if (retcode == -1) - { - error (1, errno, - "fork failed while examining conflict in `%s'", - finfo->fullname); - } + /* The timestamps differ. But if there are conflict + markers print 'C' anyway. */ + retcode = !file_has_markers (finfo); } + if (!retcode) { (void) write_letter (finfo->file, 'C', finfo->update_dir); @@ -578,15 +623,17 @@ update_fileproc (finfo) struct stat file_info; unsigned char checksum[16]; - retval = patch_file (finfo->file, finfo->repository, finfo->entries, finfo->rcs, - vers, finfo->update_dir, &docheckout, + retval = patch_file (finfo, + vers, &docheckout, &file_info, checksum); if (! docheckout) { if (server_active && retval == 0) - server_updated (finfo->file, finfo->update_dir, finfo->repository, - SERVER_PATCHED, &file_info, - checksum); + server_updated (finfo, vers, + (rcs_diff_patches + ? SERVER_RCS_DIFF + : SERVER_PATCHED), + &file_info, checksum); break; } } @@ -597,11 +644,10 @@ update_fileproc (finfo) /* Fall through. */ #endif case T_CHECKOUT: /* needs checkout */ - retval = checkout_file (finfo->file, finfo->repository, finfo->entries, finfo->rcs, - vers, finfo->update_dir); + retval = checkout_file (finfo, vers, 0); #ifdef SERVER_SUPPORT if (server_active && retval == 0) - server_updated (finfo->file, finfo->update_dir, finfo->repository, + server_updated (finfo, vers, SERVER_UPDATED, (struct stat *) NULL, (unsigned char *) NULL); #endif @@ -613,12 +659,16 @@ update_fileproc (finfo) retval = write_letter (finfo->file, 'R', finfo->update_dir); break; case T_REMOVE_ENTRY: /* needs to be un-registered */ - retval = scratch_file (finfo->file, finfo->repository, finfo->entries, finfo->update_dir); + retval = scratch_file (finfo); #ifdef SERVER_SUPPORT if (server_active && retval == 0) - server_updated (finfo->file, finfo->update_dir, finfo->repository, + { + if (vers->ts_user == NULL) + server_scratch_entry_only (); + server_updated (finfo, vers, SERVER_UPDATED, (struct stat *) NULL, (unsigned char *) NULL); + } #endif break; default: /* can't ever happen :-) */ @@ -631,11 +681,7 @@ update_fileproc (finfo) /* only try to join if things have gone well thus far */ if (retval == 0 && join_rev1) -#ifdef SERVER_SUPPORT - join_file (finfo->file, finfo->rcs, vers, finfo->update_dir, finfo->entries, finfo->repository); -#else - join_file (finfo->file, finfo->rcs, vers, finfo->update_dir, finfo->entries); -#endif + join_file (finfo, vers); /* if this directory has an ignore list, add this file to it */ if (ignlist) @@ -665,15 +711,23 @@ update_ignproc (file, dir) /* ARGSUSED */ static int -update_filesdone_proc (err, repository, update_dir) +update_filesdone_proc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { + if (rewrite_tag) + { + WriteTag (NULL, tag, date, nonbranch, update_dir, repository); + rewrite_tag = 0; + } + /* if this directory has an ignore list, process it then free it */ if (ignlist) { - ignore_files (ignlist, update_dir, update_ignproc); + ignore_files (ignlist, entries, update_dir, update_ignproc); dellist (&ignlist); } @@ -694,7 +748,7 @@ update_filesdone_proc (err, repository, update_dir) { /* If there is no CVS/Root file, add one */ if (!isfile (CVSADM_ROOT)) - Create_Root( (char *) NULL, CVSroot ); + Create_Root ((char *) NULL, CVSroot_original); } return (err); @@ -709,18 +763,20 @@ update_filesdone_proc (err, repository, update_dir) * recursion code should skip this directory. */ static Dtype -update_dirent_proc (dir, repository, update_dir) +update_dirent_proc (callerdat, dir, repository, update_dir, entries) + void *callerdat; char *dir; char *repository; char *update_dir; + List *entries; { if (ignore_directory (update_dir)) - { + { /* print the warm fuzzy message */ if (!quiet) error (0, 0, "Ignoring %s", update_dir); return R_SKIP_ALL; - } + } if (!isdir (dir)) { @@ -738,7 +794,13 @@ update_dirent_proc (dir, repository, update_dir) { /* otherwise, create the dir and appropriate adm files */ make_directory (dir); - Create_Admin (dir, update_dir, repository, tag, date); + Create_Admin (dir, update_dir, repository, tag, date, + /* This is a guess. We will rewrite it later + via WriteTag. */ + 0); + rewrite_tag = 1; + nonbranch = 0; + Subdir_Register (entries, (char *) NULL, dir); } } /* Do we need to check noexec here? */ @@ -773,8 +835,9 @@ update_dirent_proc (dir, repository, update_dir) { if (update_build_dirs) { - char tmp[PATH_MAX]; + char *tmp; + tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ENTSTAT) + 10); (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT); if (unlink_file (tmp) < 0 && ! existence_error (errno)) error (1, errno, "cannot remove file %s", tmp); @@ -782,16 +845,15 @@ update_dirent_proc (dir, repository, update_dir) if (server_active) server_clear_entstat (update_dir, repository); #endif + free (tmp); } /* keep the CVS/Tag file current with the specified arguments */ if (aflag || tag || date) { - WriteTag (dir, tag, date); -#ifdef SERVER_SUPPORT - if (server_active) - server_set_sticky (update_dir, repository, tag, date); -#endif + WriteTag (dir, tag, date, 0, update_dir, repository); + rewrite_tag = 1; + nonbranch = 0; } /* initialize the ignore list for this directory */ @@ -812,75 +874,157 @@ update_dirent_proc (dir, repository, update_dir) */ /* ARGSUSED */ static int -update_dirleave_proc (dir, err, update_dir) +update_dirleave_proc (callerdat, dir, err, update_dir, entries) + void *callerdat; char *dir; int err; char *update_dir; + List *entries; { FILE *fp; /* run the update_prog if there is one */ + /* FIXME: should be checking for errors from CVS_FOPEN and printing + them if not existence_error. */ if (err == 0 && !pipeout && !noexec && - (fp = fopen (CVSADM_UPROG, "r")) != NULL) + (fp = CVS_FOPEN (CVSADM_UPROG, "r")) != NULL) { char *cp; char *repository; - char line[MAXLINELEN]; + char *line = NULL; + size_t line_allocated = 0; repository = Name_Repository ((char *) NULL, update_dir); - if (fgets (line, sizeof (line), fp) != NULL) + if (getline (&line, &line_allocated, fp) >= 0) { if ((cp = strrchr (line, '\n')) != NULL) *cp = '\0'; run_setup ("%s %s", line, repository); - (void) printf ("%s %s: Executing '", program_name, command_name); + cvs_output (program_name, 0); + cvs_output (" ", 1); + cvs_output (command_name, 0); + cvs_output (": Executing '", 0); run_print (stdout); - (void) printf ("'\n"); + cvs_output ("'\n", 0); (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); } - (void) fclose (fp); + else if (ferror (fp)) + error (0, errno, "cannot read %s", CVSADM_UPROG); + else + error (0, 0, "unexpected end of file on %s", CVSADM_UPROG); + + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", CVSADM_UPROG); + if (line != NULL) + free (line); free (repository); } - /* FIXME: chdir ("..") loses with symlinks. */ - /* Prune empty dirs on the way out - if necessary */ - (void) chdir (".."); - if (update_prune_dirs && isemptydir (dir)) + if (strchr (dir, '/') == NULL) { - /* I'm not sure the existence_error is actually possible (except - in cases where we really should print a message), but since - this code used to ignore all errors, I'll play it safe. */ - if (unlink_file_dir (dir) < 0 && !existence_error (errno)) - error (0, errno, "cannot remove %s directory", dir); + /* FIXME: chdir ("..") loses with symlinks. */ + /* Prune empty dirs on the way out - if necessary */ + (void) CVS_CHDIR (".."); + if (update_prune_dirs && isemptydir (dir, 0)) + { + /* I'm not sure the existence_error is actually possible (except + in cases where we really should print a message), but since + this code used to ignore all errors, I'll play it safe. */ + if (unlink_file_dir (dir) < 0 && !existence_error (errno)) + error (0, errno, "cannot remove %s directory", dir); + Subdir_Deregister (entries, (char *) NULL, dir); + } } return (err); } -/* - * Returns 1 if the argument directory is completely empty, other than the - * existence of the CVS directory entry. Zero otherwise. - */ +static int isremoved PROTO ((Node *, void *)); + +/* Returns 1 if the file indicated by node has been removed. */ static int -isemptydir (dir) +isremoved (node, closure) + Node *node; + void *closure; +{ + Entnode *entdata = (Entnode*) node->data; + + /* If the first character of the version is a '-', the file has been + removed. */ + return (entdata->version && entdata->version[0] == '-') ? 1 : 0; +} + +/* Returns 1 if the argument directory is completely empty, other than the + existence of the CVS directory entry. Zero otherwise. If MIGHT_NOT_EXIST + and the directory doesn't exist, then just return 0. */ +int +isemptydir (dir, might_not_exist) char *dir; + int might_not_exist; { DIR *dirp; struct dirent *dp; - if ((dirp = opendir (dir)) == NULL) + if ((dirp = CVS_OPENDIR (dir)) == NULL) { - error (0, 0, "cannot open directory %s for empty check", dir); + if (might_not_exist && existence_error (errno)) + return 0; + error (0, errno, "cannot open directory %s for empty check", dir); return (0); } + errno = 0; while ((dp = readdir (dirp)) != NULL) { - if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 && - strcmp (dp->d_name, CVSADM) != 0) + if (strcmp (dp->d_name, ".") != 0 + && strcmp (dp->d_name, "..") != 0) { - (void) closedir (dirp); - return (0); + if (strcmp (dp->d_name, CVSADM) != 0) + { + /* An entry other than the CVS directory. The directory + is certainly not empty. */ + (void) closedir (dirp); + return (0); + } + else + { + /* The CVS directory entry. We don't have to worry about + this unless the Entries file indicates that files have + been removed, but not committed, in this directory. + (Removing the directory would prevent people from + comitting the fact that they removed the files!) */ + List *l; + int files_removed; + struct saved_cwd cwd; + + if (save_cwd (&cwd)) + error_exit (); + + if (CVS_CHDIR (dir) < 0) + error (1, errno, "cannot change directory to %s", dir); + l = Entries_Open (0); + files_removed = walklist (l, isremoved, 0); + Entries_Close (l); + + if (restore_cwd (&cwd, NULL)) + error_exit (); + free_cwd (&cwd); + + if (files_removed != 0) + { + /* There are files that have been removed, but not + committed! Do not consider the directory empty. */ + (void) closedir (dirp); + return (0); + } + } } + errno = 0; + } + if (errno != 0) + { + error (0, errno, "cannot read directory %s", dir); + (void) closedir (dirp); + return (0); } (void) closedir (dirp); return (1); @@ -890,31 +1034,26 @@ isemptydir (dir) * scratch the Entries file entry associated with a file */ static int -scratch_file (file, repository, entries, update_dir) - char *file; - char *repository; - List *entries; - char *update_dir; +scratch_file (finfo) + struct file_info *finfo; { - history_write ('W', update_dir, "", file, repository); - Scratch_Entry (entries, file); - (void) unlink_file (file); + history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository); + Scratch_Entry (finfo->entries, finfo->file); + if (unlink_file (finfo->file) < 0 && ! existence_error (errno)) + error (0, errno, "unable to remove %s", finfo->fullname); return (0); } /* - * check out a file - essentially returns the result of the fork on "co". + * Check out a file. */ static int -checkout_file (file, repository, entries, rcsnode, vers_ts, update_dir) - char *file; - char *repository; - List *entries; - RCSNode *rcsnode; +checkout_file (finfo, vers_ts, adding) + struct file_info *finfo; Vers_TS *vers_ts; - char *update_dir; + int adding; { - char backup[PATH_MAX]; + char *backup; int set_time, retval = 0; int retcode = 0; int status; @@ -923,11 +1062,17 @@ checkout_file (file, repository, entries, rcsnode, vers_ts, update_dir) /* don't screw with backup files if we're going to stdout */ if (!pipeout) { - (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, file); - if (isfile (file)) - rename_file (file, backup); + backup = xmalloc (strlen (finfo->file) + + sizeof (CVSADM) + + sizeof (CVSPREFIX) + + 10); + (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); + if (isfile (finfo->file)) + rename_file (finfo->file, backup); else - (void) unlink_file (backup); + /* If -f/-t wrappers are being used to wrap up a directory, + then backup might be a directory instead of just a file. */ + (void) unlink_file_dir (backup); } file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs); @@ -941,75 +1086,36 @@ checkout_file (file, repository, entries, rcsnode, vers_ts, update_dir) { if (!quiet) { - (void) fprintf (stderr, "\ -===================================================================\n"); - if (update_dir[0]) - (void) fprintf (stderr, "Checking out %s/%s\n", - update_dir, file); - else - (void) fprintf (stderr, "Checking out %s\n", file); - (void) fprintf (stderr, "RCS: %s\n", vers_ts->srcfile->path); - (void) fprintf (stderr, "VERS: %s\n", vers_ts->vn_rcs); - (void) fprintf (stderr, "***************\n"); + cvs_outerr ("\ +===================================================================\n\ +Checking out ", 0); + cvs_outerr (finfo->fullname, 0); + cvs_outerr ("\n\ +RCS: ", 0); + cvs_outerr (vers_ts->srcfile->path, 0); + cvs_outerr ("\n\ +VERS: ", 0); + cvs_outerr (vers_ts->vn_rcs, 0); + cvs_outerr ("\n***************\n", 0); } } - status = RCS_checkout (vers_ts->srcfile->path, - pipeout ? NULL : file, vers_ts->vn_tag, - vers_ts->options, RUN_TTY, 0, 0); + status = RCS_checkout (vers_ts->srcfile, + pipeout ? NULL : finfo->file, + vers_ts->vn_rcs, vers_ts->vn_tag, + vers_ts->options, RUN_TTY, + (RCSCHECKOUTPROC) NULL, (void *) NULL); } if (file_is_dead || status == 0) { if (!pipeout) { Vers_TS *xvers_ts; - int resurrecting; - - resurrecting = 0; - - if (file_is_dead && joining()) - { - if (RCS_getversion (vers_ts->srcfile, join_rev1, - date_rev1, 1, 0) - || (join_rev2 != NULL && - RCS_getversion (vers_ts->srcfile, join_rev2, - date_rev2, 1, 0))) - { - /* when joining, we need to get dead files checked - out. Try harder. */ - /* I think that RCS_FLAGS_FORCE is here only because - passing -f to co used to enable checking out - a dead revision in the old version of death - support which used a hacked RCS instead of using - the RCS state. */ - retcode = RCS_checkout (vers_ts->srcfile->path, file, - vers_ts->vn_rcs, - vers_ts->options, RUN_TTY, - RCS_FLAGS_FORCE, 0); - if (retcode != 0) - { - error (retcode == -1 ? 1 : 0, - retcode == -1 ? errno : 0, - "could not check out %s", file); - (void) unlink_file (backup); - return (retcode); - } - file_is_dead = 0; - resurrecting = 1; - } - else - { - /* If the file is dead and does not contain either of - the join revisions, then we don't want to check it - out. */ - return 0; - } - } if (cvswrite == TRUE && !file_is_dead - && !fileattr_get (file, "_watched")) - xchmod (file, 1); + && !fileattr_get (finfo->file, "_watched")) + xchmod (finfo->file, 1); { /* A newly checked out file is never under the spell @@ -1020,11 +1126,11 @@ checkout_file (file, repository, entries, rcsnode, vers_ts, update_dir) struct addremove_args args; - editor_set (file, getcaller (), NULL); + editor_set (finfo->file, getcaller (), NULL); memset (&args, 0, sizeof args); args.remove_temp = 1; - watch_modify_watchers (file, &args); + watch_modify_watchers (finfo->file, &args); } /* set the time from the RCS file iff it was unknown before */ @@ -1036,10 +1142,10 @@ checkout_file (file, repository, entries, rcsnode, vers_ts, update_dir) else set_time = 0; - wrap_fromcvs_process_file (file); + wrap_fromcvs_process_file (finfo->file); - xvers_ts = Version_TS (repository, options, tag, date, file, - force_tag_match, set_time, entries, rcsnode); + xvers_ts = Version_TS (finfo, options, tag, date, + force_tag_match, set_time); if (strcmp (xvers_ts->options, "-V4") == 0) xvers_ts->options[0] = '\0'; @@ -1049,31 +1155,31 @@ checkout_file (file, repository, entries, rcsnode, vers_ts, update_dir) { if (xvers_ts->vn_user != NULL) { - if (update_dir[0] == '\0') - error (0, 0, - "warning: %s is not (any longer) pertinent", - file); - else - error (0, 0, - "warning: %s/%s is not (any longer) pertinent", - update_dir, file); + error (0, 0, + "warning: %s is not (any longer) pertinent", + finfo->fullname); } - Scratch_Entry (entries, file); - if (unlink_file (file) < 0 && ! existence_error (errno)) + Scratch_Entry (finfo->entries, finfo->file); +#ifdef SERVER_SUPPORT + if (server_active && xvers_ts->ts_user == NULL) + server_scratch_entry_only (); +#endif + /* FIXME: Rather than always unlink'ing, and ignoring the + existence_error, we should do the unlink only if + vers_ts->ts_user is non-NULL. Then there would be no + need to ignore an existence_error (for example, if the + user removes the file while we are running). */ + if (unlink_file (finfo->file) < 0 && ! existence_error (errno)) { - if (update_dir[0] == '\0') - error (0, errno, "cannot remove %s", file); - else - error (0, errno, "cannot remove %s/%s", update_dir, - file); + error (0, errno, "cannot remove %s", finfo->fullname); } } else - Register (entries, file, - resurrecting ? "0" : xvers_ts->vn_rcs, - xvers_ts->ts_user, xvers_ts->options, - xvers_ts->tag, xvers_ts->date, - (char *)0); /* Clear conflict flag on fresh checkout */ + Register (finfo->entries, finfo->file, + adding ? "0" : xvers_ts->vn_rcs, + xvers_ts->ts_user, xvers_ts->options, + xvers_ts->tag, xvers_ts->date, + (char *)0); /* Clear conflict flag on fresh checkout */ /* fix up the vers structure, in case it is used by join */ if (join_rev1) @@ -1088,14 +1194,14 @@ checkout_file (file, repository, entries, rcsnode, vers_ts, update_dir) /* If this is really Update and not Checkout, recode history */ if (strcmp (command_name, "update") == 0) - history_write ('U', update_dir, xvers_ts->vn_rcs, file, - repository); + history_write ('U', finfo->update_dir, xvers_ts->vn_rcs, finfo->file, + finfo->repository); freevers_ts (&xvers_ts); if (!really_quiet && !file_is_dead) { - write_letter (file, 'U', update_dir); + write_letter (finfo->file, 'U', finfo->update_dir); } } } @@ -1104,165 +1210,190 @@ checkout_file (file, repository, entries, rcsnode, vers_ts, update_dir) int old_errno = errno; /* save errno value over the rename */ if (!pipeout && isfile (backup)) - rename_file (backup, file); + rename_file (backup, finfo->file); error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0, - "could not check out %s", file); + "could not check out %s", finfo->fullname); retval = retcode; } if (!pipeout) - (void) unlink_file (backup); + { + /* If -f/-t wrappers are being used to wrap up a directory, + then backup might be a directory instead of just a file. */ + (void) unlink_file_dir (backup); + free (backup); + } return (retval); } #ifdef SERVER_SUPPORT -/* Patch a file. Runs rcsdiff. This is only done when running as the + +/* This structure is used to pass information between patch_file and + patch_file_write. */ + +struct patch_file_data +{ + /* File name, for error messages. */ + const char *filename; + /* File to which to write. */ + FILE *fp; + /* Whether to compute the MD5 checksum. */ + int compute_checksum; + /* Data structure for computing the MD5 checksum. */ + struct MD5Context context; + /* Set if the file has a final newline. */ + int final_nl; +}; + +/* Patch a file. Runs diff. This is only done when running as the * server. The hope is that the diff will be smaller than the file * itself. */ static int -patch_file (file, repository, entries, rcsnode, vers_ts, update_dir, - docheckout, file_info, checksum) - char *file; - char *repository; - List *entries; - RCSNode *rcsnode; +patch_file (finfo, vers_ts, docheckout, file_info, checksum) + struct file_info *finfo; Vers_TS *vers_ts; - char *update_dir; int *docheckout; struct stat *file_info; unsigned char *checksum; { - char backup[PATH_MAX]; - char file1[PATH_MAX]; - char file2[PATH_MAX]; + char *backup; + char *file1; + char *file2; int retval = 0; int retcode = 0; int fail; FILE *e; + struct patch_file_data data; *docheckout = 0; - if (pipeout || joining ()) + if (noexec || pipeout || joining ()) { *docheckout = 1; return 0; } - (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, file); - if (isfile (file)) - rename_file (file, backup); + /* If this file has been marked as being binary, then never send a + patch. */ + if (strcmp (vers_ts->options, "-kb") == 0) + { + *docheckout = 1; + return 0; + } + + backup = xmalloc (strlen (finfo->file) + + sizeof (CVSADM) + + sizeof (CVSPREFIX) + + 10); + (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); + if (isfile (finfo->file)) + rename_file (finfo->file, backup); else (void) unlink_file (backup); - - (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, file); - (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, file); + + file1 = xmalloc (strlen (finfo->file) + + sizeof (CVSADM) + + sizeof (CVSPREFIX) + + 10); + (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file); + file2 = xmalloc (strlen (finfo->file) + + sizeof (CVSADM) + + sizeof (CVSPREFIX) + + 10); + (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file); fail = 0; /* We need to check out both revisions first, to see if either one has a trailing newline. Because of this, we don't use rcsdiff, but just use diff. */ - if (noexec) - retcode = 0; - else - retcode = RCS_checkout (vers_ts->srcfile->path, NULL, - vers_ts->vn_user, - vers_ts->options, file1, 0, 0); - if (retcode != 0) - fail = 1; - else - { - e = fopen (file1, "r"); - if (e == NULL) - fail = 1; - else - { - if (fseek (e, (long) -1, SEEK_END) == 0 - && getc (e) != '\n') - { - fail = 1; - } - fclose (e); - } - } + + e = CVS_FOPEN (file1, "w"); + if (e == NULL) + error (1, errno, "cannot open %s", file1); + + data.filename = file1; + data.fp = e; + data.final_nl = 0; + data.compute_checksum = 0; + + retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL, + vers_ts->vn_user, (char *) NULL, + vers_ts->options, RUN_TTY, + patch_file_write, (void *) &data); + + if (fclose (e) < 0) + error (1, errno, "cannot close %s", file1); + + if (retcode != 0 || ! data.final_nl) + fail = 1; if (! fail) { - /* Check it out into file, and then move to file2, so that we - can get the right modes into *FILE_INFO. We can't check it - out directly into file2 because co doesn't understand how - to do that. */ - retcode = RCS_checkout (vers_ts->srcfile->path, file, - vers_ts->vn_rcs, - vers_ts->options, RUN_TTY, 0, 0); - if (retcode != 0) - fail = 1; - else - { - if (!isreadable (file)) - { - /* File is dead. */ - fail = 1; - } - else - { - rename_file (file, file2); - if (cvswrite == TRUE - && !fileattr_get (file, "_watched")) - xchmod (file2, 1); - e = fopen (file2, "r"); - if (e == NULL) - fail = 1; - else - { - struct MD5Context context; - int nl; - unsigned char buf[8192]; - unsigned len; + e = CVS_FOPEN (file2, "w"); + if (e == NULL) + error (1, errno, "cannot open %s", file2); - nl = 0; + data.filename = file2; + data.fp = e; + data.final_nl = 0; + data.compute_checksum = 1; + MD5Init (&data.context); - /* Compute the MD5 checksum and make sure there is - a trailing newline. */ - MD5Init (&context); - while ((len = fread (buf, 1, sizeof buf, e)) != 0) - { - nl = buf[len - 1] == '\n'; - MD5Update (&context, buf, len); - } - MD5Final (checksum, &context); + retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL, + vers_ts->vn_rcs, (char *) NULL, + vers_ts->options, RUN_TTY, + patch_file_write, (void *) &data); - if (ferror (e) || ! nl) - { - fail = 1; - } + if (fclose (e) < 0) + error (1, errno, "cannot close %s", file2); - fclose (e); - } - } - } + if (retcode != 0 || ! data.final_nl) + fail = 1; + else + MD5Final (checksum, &data.context); } retcode = 0; if (! fail) { - /* FIXME: This whole thing with diff/patch is rather more - convoluted than necessary (lots of forks and execs, need to - worry about versions of diff and patch, etc.). Also, we - send context lines which aren't needed (in the rare case in - which the diff doesn't apply, the checksum would catches it). - Solution perhaps is to librarify the RCS routines which apply - deltas or something equivalent. */ - /* This is -c, not -u, because we have no way of knowing which - DIFF is in use. */ - run_setup ("%s -c %s %s", DIFF, file1, file2); + const char *diff_options; + + /* FIXME: It might be better to come up with a diff library + which can be shared with the diffutils. */ + /* If the client does not support the Rcs-diff command, we + send a context diff, and the client must invoke patch. + That approach was problematical for various reasons. The + new approach only requires running diff in the server; the + client can handle everything without invoking an external + program. */ + if (! rcs_diff_patches) + { + /* We use -c, not -u, because we have no way of knowing + which DIFF is in use. */ + diff_options = "-c"; + } + else + { + /* FIXME: We should use -a if diff supports it. We should + probably just copy over most or all of the diff + handling in the RCS configure script. */ + /* IMHO, we shouldn't copy over anything which even + vaguely resembles the RCS configure script. That kind of + thing tends to be ugly, slow, and fragile. It also is a + a support headache for CVS to behave differently in subtle + ways based on whether it was installed correctly. Instead we + should come up with a diff library. -kingdon, Apr 1997. */ + diff_options = "-n"; + } + run_setup ("%s %s %s %s", DIFF, diff_options, file1, file2); /* A retcode of 0 means no differences. 1 means some differences. */ - if ((retcode = run_exec (RUN_TTY, file, RUN_TTY, RUN_NORMAL)) != 0 + if ((retcode = run_exec (RUN_TTY, finfo->file, RUN_TTY, RUN_NORMAL)) != 0 && retcode != 1) { fail = 1; @@ -1273,10 +1404,24 @@ patch_file (file, repository, entries, rcsnode, vers_ts, update_dir, char buf[sizeof BINARY]; unsigned int c; + /* Stat the original RCS file, and then adjust it the way + that RCS_checkout would. FIXME: This is an abstraction + violation. */ + if (CVS_STAT (vers_ts->srcfile->path, file_info) < 0) + error (1, errno, "could not stat %s", vers_ts->srcfile->path); + if (chmod (finfo->file, + file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)) + < 0) + error (0, errno, "cannot change mode of file %s", finfo->file); + if (cvswrite == TRUE + && !fileattr_get (finfo->file, "_watched")) + xchmod (finfo->file, 1); + /* Check the diff output to make sure patch will be handle it. */ - e = fopen (file, "r"); + e = CVS_FOPEN (finfo->file, "r"); if (e == NULL) - error (1, errno, "could not open diff output file %s", file); + error (1, errno, "could not open diff output file %s", + finfo->fullname); c = fread (buf, 1, sizeof BINARY - 1, e); buf[c] = '\0'; if (strcmp (buf, BINARY) == 0) @@ -1295,28 +1440,28 @@ patch_file (file, repository, entries, rcsnode, vers_ts, update_dir, /* This stuff is just copied blindly from checkout_file. I don't really know what it does. */ - xvers_ts = Version_TS (repository, options, tag, date, file, - force_tag_match, 0, entries, rcsnode); + xvers_ts = Version_TS (finfo, options, tag, date, + force_tag_match, 0); if (strcmp (xvers_ts->options, "-V4") == 0) xvers_ts->options[0] = '\0'; - Register (entries, file, xvers_ts->vn_rcs, + Register (finfo->entries, finfo->file, xvers_ts->vn_rcs, xvers_ts->ts_user, xvers_ts->options, xvers_ts->tag, xvers_ts->date, NULL); - if (stat (file2, file_info) < 0) - error (1, errno, "could not stat %s", file2); + if (CVS_STAT (finfo->file, file_info) < 0) + error (1, errno, "could not stat %s", finfo->file); /* If this is really Update and not Checkout, recode history */ if (strcmp (command_name, "update") == 0) - history_write ('P', update_dir, xvers_ts->vn_rcs, file, - repository); + history_write ('P', finfo->update_dir, xvers_ts->vn_rcs, finfo->file, + finfo->repository); freevers_ts (&xvers_ts); if (!really_quiet) { - write_letter (file, 'P', update_dir); + write_letter (finfo->file, 'P', finfo->update_dir); } } else @@ -1324,11 +1469,11 @@ patch_file (file, repository, entries, rcsnode, vers_ts, update_dir, int old_errno = errno; /* save errno value over the rename */ if (isfile (backup)) - rename_file (backup, file); + rename_file (backup, finfo->file); if (retcode != 0 && retcode != 1) error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0, - "could not diff %s", file); + "could not diff %s", finfo->fullname); *docheckout = 1; retval = retcode; @@ -1338,9 +1483,34 @@ patch_file (file, repository, entries, rcsnode, vers_ts, update_dir, (void) unlink_file (file1); (void) unlink_file (file2); + free (backup); + free (file1); + free (file2); return (retval); } -#endif + +/* Write data to a file. Record whether the last byte written was a + newline. Optionally compute a checksum. This is called by + patch_file via RCS_checkout. */ + +static void +patch_file_write (callerdat, buffer, len) + void *callerdat; + const char *buffer; + size_t len; +{ + struct patch_file_data *data = (struct patch_file_data *) callerdat; + + if (fwrite (buffer, 1, len, data->fp) != len) + error (1, errno, "cannot write %s", data->filename); + + data->final_nl = (buffer[len - 1] == '\n'); + + if (data->compute_checksum) + MD5Update (&data->context, buffer, len); +} + +#endif /* SERVER_SUPPORT */ /* * Several of the types we process only print a bit of information consisting @@ -1373,17 +1543,14 @@ write_letter (file, letter, update_dir) * Do all the magic associated with a file which needs to be merged */ static int -merge_file (file, repository, entries, vers, update_dir) - char *file; - char *repository; - List *entries; +merge_file (finfo, vers) + struct file_info *finfo; Vers_TS *vers; - char *update_dir; { - char user[PATH_MAX]; - char backup[PATH_MAX]; + char *backup; int status; int retcode = 0; + int retval; /* * The users currently modified file is moved to a backup file name @@ -1392,26 +1559,58 @@ merge_file (file, repository, entries, vers, update_dir) * is the version of the file that the user was most up-to-date with * before the merge. */ - (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user); - if (update_dir[0]) - (void) sprintf (user, "%s/%s", update_dir, file); - else - (void) strcpy (user, file); + backup = xmalloc (strlen (finfo->file) + + strlen (vers->vn_user) + + sizeof (BAKPREFIX) + + 10); + (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user); (void) unlink_file (backup); - copy_file (file, backup); - xchmod (file, 1); + copy_file (finfo->file, backup); + xchmod (finfo->file, 1); + + if (strcmp (vers->options, "-kb") == 0) + { + /* For binary files, a merge is always a conflict. We give the + user the two files, and let them resolve it. It is possible + that we should require a "touch foo" or similar step before + we allow a checkin. */ + status = checkout_file (finfo, vers, 0); +#ifdef SERVER_SUPPORT + /* Send the new contents of the file before the message. If we + wanted to be totally correct, we would have the client write + the message only after the file has safely been written. */ + if (server_active) + { + server_copy_file (finfo->file, finfo->update_dir, + finfo->repository, backup); + server_updated (finfo, vers, SERVER_MERGED, + (struct stat *) NULL, (unsigned char *) NULL); + } +#endif + error (0, 0, "binary file needs merge"); + error (0, 0, "revision %s from repository is now in %s", + vers->vn_rcs, finfo->fullname); + error (0, 0, "file from working directory is now in %s", backup); + write_letter (finfo->file, 'C', finfo->update_dir); + + history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, + finfo->repository); + retval = 0; + goto out; + } status = RCS_merge(vers->srcfile->path, vers->options, vers->vn_user, vers->vn_rcs); if (status != 0 && status != 1) { error (0, status == -1 ? errno : 0, - "could not merge revision %s of %s", vers->vn_user, user); + "could not merge revision %s of %s", vers->vn_user, finfo->fullname); error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s", - user, backup); - rename_file (backup, file); - return (1); + finfo->fullname, backup); + rename_file (backup, finfo->file); + retval = 1; + goto out; } if (strcmp (vers->options, "-V4") == 0) @@ -1421,8 +1620,8 @@ merge_file (file, repository, entries, vers, update_dir) char *cp = 0; if (status) - cp = time_stamp (file); - Register (entries, file, vers->vn_rcs, vers->ts_rcs, vers->options, + cp = time_stamp (finfo->file); + Register (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_rcs, vers->options, vers->tag, vers->date, cp); if (cp) free (cp); @@ -1442,40 +1641,48 @@ merge_file (file, repository, entries, vers, update_dir) the message only after the file has safely been written. */ if (server_active) { - server_copy_file (file, update_dir, repository, backup); - server_updated (file, update_dir, repository, SERVER_MERGED, + server_copy_file (finfo->file, finfo->update_dir, finfo->repository, + backup); + server_updated (finfo, vers, SERVER_MERGED, (struct stat *) NULL, (unsigned char *) NULL); } #endif - if (!noexec && !xcmp (backup, file)) + if (!noexec && !xcmp (backup, finfo->file)) { printf ("%s already contains the differences between %s and %s\n", - user, vers->vn_user, vers->vn_rcs); - history_write ('G', update_dir, vers->vn_rcs, file, repository); - return (0); + finfo->fullname, vers->vn_user, vers->vn_rcs); + history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file, + finfo->repository); + retval = 0; + goto out; } if (status == 1) { if (!noexec) - error (0, 0, "conflicts found in %s", user); + error (0, 0, "conflicts found in %s", finfo->fullname); - write_letter (file, 'C', update_dir); + write_letter (finfo->file, 'C', finfo->update_dir); - history_write ('C', update_dir, vers->vn_rcs, file, repository); + history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository); } else if (retcode == -1) { - error (1, errno, "fork failed while examining update of %s", user); + error (1, errno, "fork failed while examining update of %s", + finfo->fullname); } else { - write_letter (file, 'M', update_dir); - history_write ('G', update_dir, vers->vn_rcs, file, repository); + write_letter (finfo->file, 'M', finfo->update_dir); + history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file, + finfo->repository); } - return (0); + retval = 0; + out: + free (backup); + return retval; } /* @@ -1483,20 +1690,11 @@ merge_file (file, repository, entries, vers, update_dir) * (-j option) */ static void -#ifdef SERVER_SUPPORT -join_file (file, rcsnode, vers, update_dir, entries, repository) - char *repository; -#else -join_file (file, rcsnode, vers, update_dir, entries) -#endif - char *file; - RCSNode *rcsnode; +join_file (finfo, vers) + struct file_info *finfo; Vers_TS *vers; - char *update_dir; - List *entries; { - char user[PATH_MAX]; - char backup[PATH_MAX]; + char *backup; char *options; int status; @@ -1512,24 +1710,23 @@ join_file (file, rcsnode, vers, update_dir, entries) jdate1 = date_rev1; jdate2 = date_rev2; - if (wrap_merge_is_copy (file)) + if (wrap_merge_is_copy (finfo->file)) { - /* FIXME: Should be including update_dir in message. */ error (0, 0, - "Cannot merge %s because it is a merge-by-copy file.", file); + "Cannot merge %s because it is a merge-by-copy file.", + finfo->fullname); return; } - /* determine if we need to do anything at all */ + /* Determine if we need to do anything at all. */ if (vers->srcfile == NULL || vers->srcfile->path == NULL) { return; } - /* in all cases, use two revs. */ - - /* if only one rev is specified, it becomes the second rev */ + /* If only one join revision is specified, it becomes the second + revision. */ if (jrev2 == NULL) { jrev2 = jrev1; @@ -1538,219 +1735,282 @@ join_file (file, rcsnode, vers, update_dir, entries) jdate1 = NULL; } - /* The file in the working directory doesn't exist in CVS/Entries. - FIXME: Shouldn't this case result in additional processing (if - the file was added going from rev1 to rev2, then do the equivalent - of a "cvs add")? (yes; easier said than done.. :-) */ - if (vers->vn_user == NULL) + /* Convert the second revision, walking branches and dates. */ + rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, (int *) NULL); + + /* If this is a merge of two revisions, get the first revision. + If only one join tag was specified, then the first revision is + the greatest common ancestor of the second revision and the + working file. */ + if (jrev1 != NULL) + rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, (int *) NULL); + else { - /* No merge possible YET. */ - if (jdate2 != NULL) - error (0, 0, - "file %s is present in revision %s as of %s", - file, jrev2, jdate2); + /* Note that we use vn_rcs here, since vn_user may contain a + special string such as "-nn". */ + if (vers->vn_rcs == NULL) + rev1 = NULL; + else if (rev2 == NULL) + { + /* This means that the file never existed on the branch. + It does not mean that the file was removed on the + branch: that case is represented by a dead rev2. If + the file never existed on the branch, then we have + nothing to merge, so we just return. */ + return; + } else - error (0, 0, - "file %s is present in revision %s", - file, jrev2); - return; + rev1 = gca (vers->vn_rcs, rev2); } - /* Fix for bug CVS/193: - * Used to dump core if the file had been removed on the current branch. - */ - if (strcmp(vers->vn_user, "0") == 0) + /* Handle a nonexistent or dead merge target. */ + if (rev2 == NULL || RCS_isdead (vers->srcfile, rev2)) { - error(0, 0, - "file %s has been deleted", - file); - return; - } + char *mrev; + + if (rev2 != NULL) + free (rev2); - /* convert the second rev spec, walking branches and dates. */ + /* If the first revision doesn't exist either, then there is + no change between the two revisions, so we don't do + anything. */ + if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1)) + { + if (rev1 != NULL) + free (rev1); + return; + } - rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, 0); - if (rev2 == NULL) - { - if (!quiet) + /* If we are merging two revisions, then the file was removed + between the first revision and the second one. In this + case we want to mark the file for removal. + + If we are merging one revision, then the file has been + removed between the greatest common ancestor and the merge + revision. From the perspective of the branch on to which + we ar emerging, which may be the trunk, either 1) the file + does not currently exist on the target, or 2) the file has + not been modified on the target branch since the greatest + common ancestor, or 3) the file has been modified on the + target branch since the greatest common ancestor. In case + 1 there is nothing to do. In case 2 we mark the file for + removal. In case 3 we have a conflict. + + Note that the handling is slightly different depending upon + whether one or two join targets were specified. If two + join targets were specified, we don't check whether the + file was modified since a given point. My reasoning is + that if you ask for an explicit merge between two tags, + then you want to merge in whatever was changed between + those two tags. If a file was removed between the two + tags, then you want it to be removed. However, if you ask + for a merge of a branch, then you want to merge in all + changes which were made on the branch. If a file was + removed on the branch, that is a change to the file. If + the file was also changed on the main line, then that is + also a change. These two changes--the file removal and the + modification--must be merged. This is a conflict. */ + + /* If the user file is dead, or does not exist, or has been + marked for removal, then there is nothing to do. */ + if (vers->vn_user == NULL + || vers->vn_user[0] == '-' + || RCS_isdead (vers->srcfile, vers->vn_user)) + { + if (rev1 != NULL) + free (rev1); + return; + } + + /* If the user file has been marked for addition, or has been + locally modified, then we have a conflict which we can not + resolve. No_Difference will already have been called in + this case, so comparing the timestamps is sufficient to + determine whether the file is locally modified. */ + if (strcmp (vers->vn_user, "0") == 0 + || (vers->ts_user != NULL + && strcmp (vers->ts_user, vers->ts_rcs) != 0)) { if (jdate2 != NULL) error (0, 0, - "cannot find revision %s as of %s in file %s", - jrev2, jdate2, file); + "file %s is locally modified, but has been removed in revision %s as of %s", + finfo->fullname, jrev2, jdate2); else error (0, 0, - "cannot find revision %s in file %s", - jrev2, file); + "file %s is locally modified, but has been removed in revision %s", + finfo->fullname, jrev2); + + /* FIXME: Should we arrange to return a non-zero exit + status? */ + + if (rev1 != NULL) + free (rev1); + + return; } + + /* If only one join tag was specified, and the user file has + been changed since the greatest common ancestor (rev1), + then there is a conflict we can not resolve. See above for + the rationale. */ + if (join_rev2 == NULL + && strcmp (rev1, vers->vn_user) != 0) + { + if (jdate2 != NULL) + error (0, 0, + "file %s has been modified, but has been removed in revision %s as of %s", + finfo->fullname, jrev2, jdate2); + else + error (0, 0, + "file %s has been modified, but has been removed in revision %s", + finfo->fullname, jrev2); + + /* FIXME: Should we arrange to return a non-zero exit + status? */ + + if (rev1 != NULL) + free (rev1); + + return; + } + + if (rev1 != NULL) + free (rev1); + + /* The user file exists and has not been modified. Mark it + for removal. FIXME: If we are doing a checkout, this has + the effect of first checking out the file, and then + removing it. It would be better to just register the + removal. */ +#ifdef SERVER_SUPPORT + if (server_active) + { + server_scratch (finfo->file); + server_updated (finfo, vers, SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); + } +#endif + mrev = xmalloc (strlen (vers->vn_user) + 2); + sprintf (mrev, "-%s", vers->vn_user); + Register (finfo->entries, finfo->file, mrev, vers->ts_rcs, + vers->options, vers->tag, vers->date, vers->ts_conflict); + free (mrev); + /* We need to check existence_error here because if we are + running as the server, and the file is up to date in the + working directory, the client will not have sent us a copy. */ + if (unlink_file (finfo->file) < 0 && ! existence_error (errno)) + error (0, errno, "cannot remove file %s", finfo->fullname); +#ifdef SERVER_SUPPORT + if (server_active) + server_checked_in (finfo->file, finfo->update_dir, + finfo->repository); +#endif + if (! really_quiet) + error (0, 0, "scheduling %s for removal", finfo->fullname); + return; } - /* skip joining identical revs */ - if (strcmp (rev2, vers->vn_user) == 0) + /* If the target of the merge is the same as the working file + revision, then there is nothing to do. */ + if (vers->vn_user != NULL && strcmp (rev2, vers->vn_user) == 0) { - /* No merge necessary. */ + if (rev1 != NULL) + free (rev1); free (rev2); return; } - if (jrev1 == NULL) + /* If rev1 is dead or does not exist, then the file was added + between rev1 and rev2. */ + if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1)) { - char *tst; - /* if the first rev is missing, then it is implied to be the - greatest common ancestor of both the join rev, and the - checked out rev. */ - - /* FIXME: What is this check for '!' about? If it is legal to - have '!' in the first character of vn_user, it isn't - documented at struct vers_ts in cvs.h. */ - tst = vers->vn_user; - if (*tst == '!') - { - /* file was dead. merge anyway and pretend it's been - added. */ - ++tst; - Register (entries, file, "0", vers->ts_user, vers->options, - vers->tag, (char *) 0, (char *) 0); - } - rev1 = gca (tst, rev2); - if (rev1 == NULL) - { - /* this should not be possible */ - error (0, 0, "bad gca"); - abort(); - } + if (rev1 != NULL) + free (rev1); + free (rev2); - tst = RCS_gettag (vers->srcfile, rev2, 1, 0); - if (tst == NULL) + /* If the file does not exist in the working directory, then + we can just check out the new revision and mark it for + addition. */ + if (vers->vn_user == NULL) { - /* this should not be possible. */ - error (0, 0, "cannot find gca"); - abort(); - } + Vers_TS *xvers; - free (tst); + xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0); + + /* FIXME: If checkout_file fails, we should arrange to + return a non-zero exit status. */ + status = checkout_file (finfo, xvers, 1); + +#ifdef SERVER_SUPPORT + if (server_active && status == 0) + server_updated (finfo, xvers, + SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); +#endif + + freevers_ts (&xvers); - /* these two cases are noops */ - if (strcmp (rev1, rev2) == 0) - { - free (rev1); - free (rev2); return; } + + /* The file currently exists in the working directory, so we + have a conflict which we can not resolve. Note that this + is true even if the file is marked for addition or removal. */ + + if (jdate2 != NULL) + error (0, 0, + "file %s exists, but has been added in revision %s as of %s", + finfo->fullname, jrev2, jdate2); + else + error (0, 0, + "file %s exists, but has been added in revision %s", + finfo->fullname, jrev2); + + return; } - else - { - /* otherwise, convert the first rev spec, walking branches and - dates. */ - rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, 0); - if (rev1 == NULL) - { - if (!quiet) { - if (jdate1 != NULL) - error (0, 0, - "cannot find revision %s as of %s in file %s", - jrev1, jdate1, file); - else - error (0, 0, - "cannot find revision %s in file %s", - jrev1, file); - } - return; - } + /* If the two merge revisions are the same, then there is nothing + to do. */ + if (strcmp (rev1, rev2) == 0) + { + free (rev1); + free (rev2); + return; } - /* do the join */ + /* If there is no working file, then we can't do the merge. */ + if (vers->vn_user == NULL) + { + free (rev1); + free (rev2); -#if 0 - dome { - /* special handling when two revisions are specified */ - if (join_rev1 && join_rev2) - { - rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1, 0); - if (rev == NULL) - { - if (!quiet && date_rev2 == NULL) - error (0, 0, - "cannot find revision %s in file %s", join_rev2, file); - return; - } - - baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0); - if (baserev == NULL) - { - if (!quiet && date_rev1 == NULL) - error (0, 0, - "cannot find revision %s in file %s", join_rev1, file); - free (rev); - return; - } - - /* - * nothing to do if: - * second revision matches our BASE revision (vn_user) && - * both revisions are on the same branch - */ - if (strcmp (vers->vn_user, rev) == 0 && - numdots (baserev) == numdots (rev)) - { - /* might be the same branch. take a real look */ - char *dot = strrchr (baserev, '.'); - int len = (dot - baserev) + 1; - - if (strncmp (baserev, rev, len) == 0) - return; - } - } + if (jdate2 != NULL) + error (0, 0, + "file %s is present in revision %s as of %s", + finfo->fullname, jrev2, jdate2); else - { - rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0); - if (rev == NULL) - return; - if (strcmp (rev, vers->vn_user) == 0) /* no merge necessary */ - { - free (rev); - return; - } - - baserev = RCS_whatbranch (file, join_rev1, rcsnode); - if (baserev) - { - char *cp; - - /* we get a branch -- turn it into a revision, or NULL if trunk */ - if ((cp = strrchr (baserev, '.')) == NULL) - { - free (baserev); - baserev = (char *) NULL; - } - else - *cp = '\0'; - } - } - if (baserev && strcmp (baserev, rev) == 0) - { - /* they match -> nothing to do */ - free (rev); - free (baserev); - return; - } - } -#endif + error (0, 0, + "file %s is present in revision %s", + finfo->fullname, jrev2); - /* OK, so we have two revisions; continue on */ + /* FIXME: Should we arrange to return a non-zero exit status? */ + + return; + } #ifdef SERVER_SUPPORT - if (server_active && !isreadable (file)) + if (server_active && !isreadable (finfo->file)) { int retcode; /* The file is up to date. Need to check out the current contents. */ - retcode = RCS_checkout (vers->srcfile->path, "", vers->vn_user, NULL, - RUN_TTY, 0, 0); + retcode = RCS_checkout (vers->srcfile, finfo->file, + vers->vn_user, (char *) NULL, + (char *) NULL, RUN_TTY, + (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) error (1, retcode == -1 ? errno : 0, - "failed to check out %s file", file); + "failed to check out %s file", finfo->fullname); } #endif @@ -1761,15 +2021,15 @@ join_file (file, rcsnode, vers, update_dir, entries) * is the version of the file that the user was most up-to-date with * before the merge. */ - (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user); - if (update_dir[0]) - (void) sprintf (user, "%s/%s", update_dir, file); - else - (void) strcpy (user, file); + backup = xmalloc (strlen (finfo->file) + + strlen (vers->vn_user) + + sizeof (BAKPREFIX) + + 10); + (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user); (void) unlink_file (backup); - copy_file (file, backup); - xchmod (file, 1); + copy_file (finfo->file, backup); + xchmod (finfo->file, 1); options = vers->options; #ifdef HAVE_RCS5 @@ -1783,10 +2043,10 @@ join_file (file, rcsnode, vers, update_dir, entries) if (status != 0 && status != 1) { error (0, status == -1 ? errno : 0, - "could not merge revision %s of %s", rev2, user); + "could not merge revision %s of %s", rev2, finfo->fullname); error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s", - user, backup); - rename_file (backup, file); + finfo->fullname, backup); + rename_file (backup, finfo->file); } free (rev1); free (rev2); @@ -1806,8 +2066,9 @@ join_file (file, rcsnode, vers, update_dir, entries) char *cp = 0; if (status) - cp = time_stamp (file); - Register (entries, file, vers->vn_rcs, vers->ts_rcs, vers->options, + cp = time_stamp (finfo->file); + Register (finfo->entries, finfo->file, + vers->vn_rcs, vers->ts_rcs, vers->options, vers->tag, vers->date, cp); if (cp) free(cp); @@ -1816,11 +2077,13 @@ join_file (file, rcsnode, vers, update_dir, entries) #ifdef SERVER_SUPPORT if (server_active) { - server_copy_file (file, update_dir, repository, backup); - server_updated (file, update_dir, repository, SERVER_MERGED, + server_copy_file (finfo->file, finfo->update_dir, finfo->repository, + backup); + server_updated (finfo, vers, SERVER_MERGED, (struct stat *) NULL, (unsigned char *) NULL); } #endif + free (backup); } int diff --git a/contrib/cvs/src/update.h b/contrib/cvs/src/update.h index bad6562..ba71310 100644 --- a/contrib/cvs/src/update.h +++ b/contrib/cvs/src/update.h @@ -8,14 +8,11 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ int do_update PROTO((int argc, char *argv[], char *xoptions, char *xtag, char *xdate, int xforce, int local, int xbuild, int xaflag, int xprune, int xpipeout, int which, char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir)); int joining PROTO((void)); +extern int isemptydir PROTO ((char *dir, int might_not_exist)); diff --git a/contrib/cvs/src/vers_ts.c b/contrib/cvs/src/vers_ts.c index 34983a1..03a0036 100644 --- a/contrib/cvs/src/vers_ts.c +++ b/contrib/cvs/src/vers_ts.c @@ -9,7 +9,7 @@ #include "cvs.h" #ifdef SERVER_SUPPORT -static void time_stamp_server PROTO((char *, Vers_TS *)); +static void time_stamp_server PROTO((char *, Vers_TS *, Entnode *)); #endif /* @@ -18,22 +18,19 @@ static void time_stamp_server PROTO((char *, Vers_TS *)); * the current source control file - preparsed for our pleasure. */ Vers_TS * -Version_TS (repository, options, tag, date, user, force_tag_match, - set_time, entries, rcs) - char *repository; +Version_TS (finfo, options, tag, date, force_tag_match, set_time) + struct file_info *finfo; char *options; char *tag; char *date; - char *user; int force_tag_match; int set_time; - List *entries; - RCSNode *rcs; { Node *p; RCSNode *rcsdata; Vers_TS *vers_ts; struct stickydirtag *sdtp; + Entnode *entdata; /* get a new Vers_TS struct */ vers_ts = (Vers_TS *) xmalloc (sizeof (Vers_TS)); @@ -44,40 +41,51 @@ Version_TS (repository, options, tag, date, user, force_tag_match, * if entries is NULL, there is no entries file so don't bother trying to * look it up (used by checkout -P) */ - if (entries == NULL) + if (finfo->entries == NULL) { sdtp = NULL; p = NULL; } else { - p = findnode_fn (entries, user); - sdtp = (struct stickydirtag *) entries->list->data; /* list-private */ + p = findnode_fn (finfo->entries, finfo->file); + sdtp = (struct stickydirtag *) finfo->entries->list->data; /* list-private */ } + entdata = NULL; if (p != NULL) { - Entnode *entdata = (Entnode *) p->data; + entdata = (Entnode *) p->data; - vers_ts->vn_user = xstrdup (entdata->version); - vers_ts->ts_rcs = xstrdup (entdata->timestamp); - vers_ts->ts_conflict = xstrdup (entdata->conflict); - if (!tag) - { - if (!(sdtp && sdtp->aflag)) - vers_ts->tag = xstrdup (entdata->tag); - } - if (!date) - { - if (!(sdtp && sdtp->aflag)) - vers_ts->date = xstrdup (entdata->date); - } - if (!options || (options && *options == '\0')) +#ifdef SERVER_SUPPORT + /* An entries line with "D" in the timestamp indicates that the + client sent Is-modified without sending Entry. So we want to + use the entries line for the sole purpose of telling + time_stamp_server what is up; we don't want the rest of CVS + to think there is an entries line. */ + if (strcmp (entdata->timestamp, "D") != 0) +#endif { - if (!(sdtp && sdtp->aflag)) - vers_ts->options = xstrdup (entdata->options); + vers_ts->vn_user = xstrdup (entdata->version); + vers_ts->ts_rcs = xstrdup (entdata->timestamp); + vers_ts->ts_conflict = xstrdup (entdata->conflict); + if (!tag) + { + if (!(sdtp && sdtp->aflag)) + vers_ts->tag = xstrdup (entdata->tag); + } + if (!date) + { + if (!(sdtp && sdtp->aflag)) + vers_ts->date = xstrdup (entdata->date); + } + if (!options || (options && *options == '\0')) + { + if (!(sdtp && sdtp->aflag)) + vers_ts->options = xstrdup (entdata->options); + } + vers_ts->entdata = entdata; } - vers_ts->entdata = entdata; } /* @@ -88,15 +96,13 @@ Version_TS (repository, options, tag, date, user, force_tag_match, vers_ts->options = xstrdup (options); else if (!vers_ts->options) { - if (sdtp && sdtp->aflag == 0) - vers_ts->options = xstrdup (sdtp->options); - else if (rcs != NULL) + if (finfo->rcs != NULL) { /* If no keyword expansion was specified on command line, use whatever was in the rcs file (if there is one). This is how we, if we are the server, tell the client whether a file is binary. */ - char *rcsexpand = RCS_getexpand (rcs); + char *rcsexpand = RCS_getexpand (finfo->rcs); if (rcsexpand != NULL) { vers_ts->options = xmalloc (strlen (rcsexpand) + 3); @@ -120,19 +126,22 @@ Version_TS (repository, options, tag, date, user, force_tag_match, else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0)) { if (!vers_ts->tag) + { vers_ts->tag = xstrdup (sdtp->tag); + vers_ts->nonbranch = sdtp->nonbranch; + } if (!vers_ts->date) vers_ts->date = xstrdup (sdtp->date); } /* Now look up the info on the source controlled file */ - if (rcs != NULL) + if (finfo->rcs != NULL) { - rcsdata = rcs; + rcsdata = finfo->rcs; rcsdata->refcount++; } - else if (repository != NULL) - rcsdata = RCS_parse (user, repository); + else if (finfo->repository != NULL) + rcsdata = RCS_parse (finfo->file, finfo->repository); else rcsdata = NULL; @@ -148,21 +157,17 @@ Version_TS (repository, options, tag, date, user, force_tag_match, } else { + int simple; + vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag, - vers_ts->date, force_tag_match, 1); + vers_ts->date, force_tag_match, + &simple); if (vers_ts->vn_rcs == NULL) vers_ts->vn_tag = NULL; + else if (simple) + vers_ts->vn_tag = xstrdup (vers_ts->tag); else - { - char *colon = strchr (vers_ts->vn_rcs, ':'); - if (colon) - { - vers_ts->vn_tag = xstrdup (colon+1); - *colon = '\0'; - } - else - vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs); - } + vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs); } /* @@ -178,19 +183,19 @@ Version_TS (repository, options, tag, date, user, force_tag_match, if (vers_ts->vn_rcs && (t.actime = t.modtime = RCS_getrevtime (rcsdata, vers_ts->vn_rcs, (char *) 0, 0)) != -1) - (void) utime (user, &t); + (void) utime (finfo->file, &t); } } /* get user file time-stamp in ts_user */ - if (entries != (List *) NULL) + if (finfo->entries != (List *) NULL) { #ifdef SERVER_SUPPORT if (server_active) - time_stamp_server (user, vers_ts); + time_stamp_server (finfo->file, vers_ts, entdata); else #endif - vers_ts->ts_user = time_stamp (user); + vers_ts->ts_user = time_stamp (finfo->file); } return (vers_ts); @@ -205,50 +210,44 @@ Version_TS (repository, options, tag, date, user, force_tag_match, #define mark_unchanged(V) ((V)->ts_user = xstrdup ((V)->ts_rcs)) static void -time_stamp_server (file, vers_ts) +time_stamp_server (file, vers_ts, entdata) char *file; Vers_TS *vers_ts; + Entnode *entdata; { struct stat sb; char *cp; - if (stat (file, &sb) < 0) + if ( CVS_STAT (file, &sb) < 0) { if (! existence_error (errno)) error (1, errno, "cannot stat temp file"); - if (use_unchanged) - { - /* Missing file means lost or unmodified; check entries - file to see which. - XXX FIXME - If there's no entries file line, we - wouldn't be getting the file at all, so consider it - lost. I don't know that that's right, but it's not - clear to me that either choice is. Besides, would we - have an RCS string in that case anyways? */ - if (vers_ts->entdata == NULL) - mark_lost (vers_ts); - else if (vers_ts->entdata->timestamp - && vers_ts->entdata->timestamp[0] == '=') - mark_unchanged (vers_ts); - else - mark_lost (vers_ts); - } - else - { - /* Missing file in the temp directory means that the file - was not modified. */ + /* Missing file means lost or unmodified; check entries + file to see which. + + XXX FIXME - If there's no entries file line, we + wouldn't be getting the file at all, so consider it + lost. I don't know that that's right, but it's not + clear to me that either choice is. Besides, would we + have an RCS string in that case anyways? */ + if (entdata == NULL) + mark_lost (vers_ts); + else if (entdata->timestamp + && entdata->timestamp[0] == '=') mark_unchanged (vers_ts); - } + else if (entdata->timestamp != NULL + && (entdata->timestamp[0] == 'M' + || entdata->timestamp[0] == 'D') + && entdata->timestamp[1] == '\0') + vers_ts->ts_user = xstrdup ("Is-modified"); + else + mark_lost (vers_ts); } else if (sb.st_mtime == 0) { - if (use_unchanged) - /* We shouldn't reach this case any more! */ - abort (); - - /* Special code used by server.c to indicate the file was lost. */ - mark_lost (vers_ts); + /* We shouldn't reach this case any more! */ + abort (); } else { @@ -290,7 +289,7 @@ time_stamp (file) char *cp; char *ts; - if (stat (file, &sb) < 0) + if ( CVS_STAT (file, &sb) < 0) { ts = NULL; } diff --git a/contrib/cvs/src/version.c b/contrib/cvs/src/version.c index 4848e82..606c43d 100644 --- a/contrib/cvs/src/version.c +++ b/contrib/cvs/src/version.c @@ -12,7 +12,7 @@ #include "cvs.h" -char *version_string = "\nConcurrent Versions System (CVS) 1.8.1"; +char *version_string = "\nConcurrent Versions System (CVS) 1.9.9"; #ifdef CLIENT_SUPPORT #ifdef SERVER_SUPPORT diff --git a/contrib/cvs/src/watch.c b/contrib/cvs/src/watch.c index 0873489..9bb50d0 100644 --- a/contrib/cvs/src/watch.c +++ b/contrib/cvs/src/watch.c @@ -8,11 +8,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ #include "cvs.h" #include "edit.h" @@ -21,10 +17,11 @@ const char *const watch_usage[] = { - "Usage: %s %s [on|off|add|remove] [-l] [-a action] [files...]\n", + "Usage: %s %s [on|off|add|remove] [-lR] [-a action] [files...]\n", "on/off: turn on/off read-only checkouts of files\n", "add/remove: add or remove notification on actions\n", "-l (on/off/add/remove): Local directory only, not recursive\n", + "-R (on/off/add/remove): Process directories recursively\n", "-a (add/remove): Specify what actions, one of\n", " edit,unedit,commit,all,none\n", NULL @@ -215,23 +212,28 @@ watch_modify_watchers (file, what) free (mynewattr); } -static int addremove_fileproc PROTO ((struct file_info *finfo)); +static int addremove_fileproc PROTO ((void *callerdat, + struct file_info *finfo)); static int -addremove_fileproc (finfo) +addremove_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { watch_modify_watchers (finfo->file, &the_args); return 0; } -static int addremove_filesdoneproc PROTO ((int, char *, char *)); +static int addremove_filesdoneproc PROTO ((void *, int, char *, char *, + List *)); static int -addremove_filesdoneproc (err, repository, update_dir) +addremove_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { if (the_args.setting_default) watch_modify_watchers (NULL, &the_args); @@ -255,13 +257,16 @@ watch_addremove (argc, argv) the_args.edit = 0; the_args.unedit = 0; optind = 1; - while ((c = getopt (argc, argv, "la:")) != -1) + while ((c = getopt (argc, argv, "+lRa:")) != -1) { switch (c) { case 'l': local = 1; break; + case 'R': + local = 0; + break; case 'a': a_omitted = 0; if (strcmp (optarg, "edit") == 0) @@ -332,10 +337,7 @@ watch_addremove (argc, argv) send_arg ("none"); } send_file_names (argc, argv, SEND_EXPAND_WILD); - /* FIXME: We shouldn't have to send current files, but I'm not sure - whether it works. So send the files -- - it's slower but it works. */ - send_files (argc, argv, local, 0); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); send_to_server (the_args.adding ? "watch-add\012" : "watch-remove\012", 0); @@ -348,11 +350,11 @@ watch_addremove (argc, argv) lock_tree_for_write (argc, argv, local, 0); err = start_recursion (addremove_fileproc, addremove_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 1, 0); + 1); - lock_tree_cleanup (); + Lock_Cleanup (); return err; } @@ -412,14 +414,18 @@ watch (argc, argv) static const char *const watchers_usage[] = { - "Usage: %s %s [files...]\n", + "Usage: %s %s [-lR] [files...]\n", + "\t-l\tProcess this directory only (not recursive).\n", + "\t-R\tProcess directories recursively.\n", NULL }; -static int watchers_fileproc PROTO ((struct file_info *finfo)); +static int watchers_fileproc PROTO ((void *callerdat, + struct file_info *finfo)); static int -watchers_fileproc (finfo) +watchers_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *them; @@ -480,13 +486,16 @@ watchers (argc, argv) usage (watchers_usage); optind = 1; - while ((c = getopt (argc, argv, "l")) != -1) + while ((c = getopt (argc, argv, "+lR")) != -1) { switch (c) { case 'l': local = 1; break; + case 'R': + local = 0; + break; case '?': default: usage (watchers_usage); @@ -505,17 +514,14 @@ watchers (argc, argv) if (local) send_arg ("-l"); send_file_names (argc, argv, SEND_EXPAND_WILD); - /* FIXME: We shouldn't have to send current files, but I'm not sure - whether it works. So send the files -- - it's slower but it works. */ - send_files (argc, argv, local, 0); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); send_to_server ("watchers\012", 0); return get_responses_and_close (); } #endif /* CLIENT_SUPPORT */ return start_recursion (watchers_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, - 1, 0); + 1); } diff --git a/contrib/cvs/src/watch.h b/contrib/cvs/src/watch.h index d279c71..fa34ef5 100644 --- a/contrib/cvs/src/watch.h +++ b/contrib/cvs/src/watch.h @@ -8,11 +8,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + GNU General Public License for more details. */ extern const char *const watch_usage[]; diff --git a/contrib/cvs/src/wrapper.c b/contrib/cvs/src/wrapper.c index 8a6ff94..13e7e43 100644 --- a/contrib/cvs/src/wrapper.c +++ b/contrib/cvs/src/wrapper.c @@ -1,4 +1,15 @@ +/* This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + #include "cvs.h" +#include "getline.h" /* Original Author: athan@morgan.com <Andrew C. Athan> 2/1/94 @@ -22,6 +33,7 @@ -f from cvs filter value: path to filter -t to cvs filter value: path to filter -m update methodology value: MERGE or COPY + -k default -k rcs option to use on import or add and value is a single-quote delimited value. @@ -34,7 +46,7 @@ typedef struct { char *wildCard; char *tocvsFilter; char *fromcvsFilter; - char *conflictHook; + char *rcsOption; WrapMergeMethod mergeMethod; } WrapperEntry; @@ -44,7 +56,16 @@ static WrapperEntry **wrap_saved_list=NULL; static int wrap_size=0; static int wrap_count=0; static int wrap_tempcount=0; + +/* FIXME: wrap_saved_count is never set to any non-zero value. + wrap_name_has and wrap_matching_entry should be using + wrap_tempcount instead. I believe the consequence of this is that + .cvswrappers files are ignored (that was my own experience when I + tried to use one). If this bug is fixed, would be nice to write a + sanity.sh testcase for .cvswrappers files. */ + static int wrap_saved_count=0; + static int wrap_saved_tempcount=0; #define WRAPPER_GROW 8 @@ -58,27 +79,81 @@ void wrap_restore_saved PROTO((void)); void wrap_setup() { - char file[PATH_MAX]; struct passwd *pw; - /* Then add entries found in repository, if it exists */ - (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_WRAPPER); - if (isfile (file)){ - wrap_add_file(file,0); +#ifdef CLIENT_SUPPORT + if (!client_active) +#endif + { + char *file; + + file = xmalloc (strlen (CVSroot_directory) + + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_WRAPPER) + + 10); + /* Then add entries found in repository, if it exists. */ + (void) sprintf (file, "%s/%s/%s", CVSroot_directory, CVSROOTADM, + CVSROOTADM_WRAPPER); + if (isfile (file)) + { + wrap_add_file(file,0); + } + free (file); } - /* Then add entries found in home dir, (if user has one) and file exists */ - if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir){ + /* Then add entries found in home dir, (if user has one) and file + exists. (FIXME: I think this probably should be using + get_homedir, i.e. $HOME). */ + if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir) + { + char *file; + + file = xmalloc (strlen (pw->pw_dir) + sizeof (CVSDOTWRAPPER) + 10); (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTWRAPPER); - if (isfile (file)){ + if (isfile (file)) + { wrap_add_file (file, 0); } + free (file); } - /* Then add entries found in CVSWRAPPERS environment variable. */ + /* Then add entries found in CVSWRAPPERS environment variable. */ wrap_add (getenv (WRAPPER_ENV), 0); } +#ifdef CLIENT_SUPPORT +/* Send -W arguments for the wrappers to the server. The command must + be one that accepts them (e.g. update, import). */ +void +wrap_send () +{ + int i; + + for (i = 0; i < wrap_count + wrap_tempcount; ++i) + { + if (wrap_list[i]->tocvsFilter != NULL + || wrap_list[i]->fromcvsFilter != NULL) + /* For greater studliness we would print the offending option + and (more importantly) where we found it. */ + error (0, 0, "\ +-t and -f wrapper options are not supported remotely; ignored"); + if (wrap_list[i]->mergeMethod == WRAP_COPY) + /* For greater studliness we would print the offending option + and (more importantly) where we found it. */ + error (0, 0, "\ +-m wrapper option is not supported remotely; ignored"); + if (wrap_list[i]->rcsOption != NULL) + { + send_to_server ("Argument -W\012Argument ", 0); + send_to_server (wrap_list[i]->wildCard, 0); + send_to_server (" -k '", 0); + send_to_server (wrap_list[i]->rcsOption, 0); + send_to_server ("'\012", 0); + } + } +} +#endif /* CLIENT_SUPPORT */ + /* * Open a file and read lines, feeding each line to a line parser. Arrange * for keeping a temporary list of wrappers at the end, if the "temp" @@ -90,17 +165,28 @@ wrap_add_file (file, temp) int temp; { FILE *fp; - char line[1024]; - - wrap_restore_saved(); - wrap_kill_temp(); - - /* load the file */ - if (!(fp = fopen (file, "r"))) + char *line = NULL; + size_t line_allocated = 0; + + wrap_restore_saved (); + wrap_kill_temp (); + + /* Load the file. */ + fp = CVS_FOPEN (file, "r"); + if (fp == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot open %s", file); return; - while (fgets (line, sizeof (line), fp)) + } + while (getline (&line, &line_allocated, fp) >= 0) wrap_add (line, temp); - (void) fclose (fp); + if (line) + free (line); + if (ferror (fp)) + error (0, errno, "cannot read %s", file); + if (fclose (fp) == EOF) + error (0, errno, "cannot close %s", file); } void @@ -132,13 +218,13 @@ void wrap_free_entry_internal(e) WrapperEntry *e; { - free(e->wildCard); - if(e->tocvsFilter) - free(e->tocvsFilter); - if(e->fromcvsFilter) - free(e->fromcvsFilter); - if(e->conflictHook) - free(e->conflictHook); + free (e->wildCard); + if (e->tocvsFilter) + free (e->tocvsFilter); + if (e->fromcvsFilter) + free (e->fromcvsFilter); + if (e->rcsOption) + free (e->rcsOption); } void @@ -208,7 +294,15 @@ wrap_add (line, isTemp) for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line) ; - if(line==temp+1) + /* This used to "break;" (ignore the option) if there was a + single character between the single quotes (I'm guessing + that was accidental). Now it "break;"s if there are no + characters. I'm not sure either behavior is particularly + necessary--the current options might not require '' + arguments, but surely some future option legitimately + might. Also I'm not sure that ignoring the option is a + swift way to handle syntax errors in general. */ + if (line==temp) break; ctemp=*line; @@ -232,21 +326,19 @@ wrap_add (line, isTemp) if (!e.tocvsFilter) error (1, 0, "Correct above errors first"); break; - case 'c': - if(e.conflictHook) - free(e.conflictHook); - /* FIXME: error message should say where the bad value - came from. */ - e.conflictHook=expand_path (temp, "<wrapper>", 0); - if (!e.conflictHook) - error (1, 0, "Correct above errors first"); - break; case 'm': + /* FIXME: look into whether this option is still relevant given + the 24 Jun 96 change to merge_file. */ if(*temp=='C' || *temp=='c') e.mergeMethod=WRAP_COPY; else e.mergeMethod=WRAP_MERGE; break; + case 'k': + if (e.rcsOption) + free (e.rcsOption); + e.rcsOption = xstrdup (temp); + break; default: break; } @@ -281,8 +373,8 @@ wrap_add_entry(e, temp) wrap_list[x]->wildCard=e->wildCard; wrap_list[x]->fromcvsFilter=e->fromcvsFilter; wrap_list[x]->tocvsFilter=e->tocvsFilter; - wrap_list[x]->conflictHook=e->conflictHook; wrap_list[x]->mergeMethod=e->mergeMethod; + wrap_list[x]->rcsOption = e->rcsOption; } /* Return 1 if the given filename is a wrapper filename */ @@ -303,8 +395,8 @@ wrap_name_has (name,has) case WRAP_FROMCVS: temp=wrap_list[x]->fromcvsFilter; break; - case WRAP_CONFLICT: - temp=wrap_list[x]->conflictHook; + case WRAP_RCSOPTION: + temp = wrap_list[x]->rcsOption; break; default: abort (); @@ -317,7 +409,9 @@ wrap_name_has (name,has) return (0); } -WrapperEntry * +static WrapperEntry *wrap_matching_entry PROTO ((const char *)); + +static WrapperEntry * wrap_matching_entry (name) const char *name; { @@ -329,17 +423,46 @@ wrap_matching_entry (name) return (WrapperEntry *)NULL; } +/* Return the RCS options for FILENAME in a newly malloc'd string. If + ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise + just give the option itself (e.g. "b"). */ +char * +wrap_rcsoption (filename, asflag) + const char *filename; + int asflag; +{ + WrapperEntry *e = wrap_matching_entry (filename); + char *buf; + + if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0')) + return NULL; + + buf = xmalloc (strlen (e->rcsOption) + 3); + if (asflag) + { + strcpy (buf, "-k"); + strcat (buf, e->rcsOption); + } + else + { + strcpy (buf, e->rcsOption); + } + return buf; +} + char * wrap_tocvs_process_file(fileName) const char *fileName; { WrapperEntry *e=wrap_matching_entry(fileName); - static char buf[L_tmpnam+1]; + static char *buf = NULL; if(e==NULL || e->tocvsFilter==NULL) return NULL; - tmpnam(buf); + if (buf != NULL) + free (buf); + buf = cvs_temp_name (); run_setup(e->tocvsFilter,fileName,buf); run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY ); @@ -358,17 +481,16 @@ wrap_merge_is_copy (fileName) return 1; } -char * +void wrap_fromcvs_process_file(fileName) const char *fileName; { WrapperEntry *e=wrap_matching_entry(fileName); - static char buf[PATH_MAX]; if(e==NULL || e->fromcvsFilter==NULL) - return NULL; + return; run_setup(e->fromcvsFilter,fileName); run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL ); - return buf; + return; } diff --git a/contrib/cvs/src/zlib.c b/contrib/cvs/src/zlib.c new file mode 100644 index 0000000..02ad70c --- /dev/null +++ b/contrib/cvs/src/zlib.c @@ -0,0 +1,429 @@ +/* zlib.c --- interface to the zlib compression library + Ian Lance Taylor <ian@cygnus.com> + + This file is part of GNU CVS. + + GNU CVS is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* The routines in this file are the interface between the CVS + client/server support and the zlib compression library. */ + +#include <assert.h> +#include "cvs.h" +#include "buffer.h" + +#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) + +#include "zlib.h" + +/* OS/2 doesn't have EIO. FIXME: this whole notion of turning + a different error into EIO strikes me as pretty dubious. */ +#if !defined (EIO) +#define EIO EBADPOS +#endif + +/* The compression interface is built upon the buffer data structure. + We provide a buffer type which compresses or decompresses the data + which passes through it. An input buffer decompresses the data + read from an underlying buffer, and an output buffer compresses the + data before writing it to an underlying buffer. */ + +/* This structure is the closure field of the buffer. */ + +struct compress_buffer +{ + /* The underlying buffer. */ + struct buffer *buf; + /* The compression information. */ + z_stream zstr; +}; + +static void compress_error PROTO((int, int, z_stream *, const char *)); +static int compress_buffer_input PROTO((void *, char *, int, int, int *)); +static int compress_buffer_output PROTO((void *, const char *, int, int *)); +static int compress_buffer_flush PROTO((void *)); +static int compress_buffer_block PROTO((void *, int)); +static int compress_buffer_shutdown_input PROTO((void *)); +static int compress_buffer_shutdown_output PROTO((void *)); + +/* Report an error from one of the zlib functions. */ + +static void +compress_error (status, zstatus, zstr, msg) + int status; + int zstatus; + z_stream *zstr; + const char *msg; +{ + int hold_errno; + const char *zmsg; + char buf[100]; + + hold_errno = errno; + + zmsg = zstr->msg; + if (zmsg == NULL) + { + sprintf (buf, "error %d", zstatus); + zmsg = buf; + } + + error (status, + zstatus == Z_ERRNO ? hold_errno : 0, + "%s: %s", msg, zmsg); +} + +/* Create a compression buffer. */ + +struct buffer * +compress_buffer_initialize (buf, input, level, memory) + struct buffer *buf; + int input; + int level; + void (*memory) PROTO((struct buffer *)); +{ + struct compress_buffer *n; + int zstatus; + + n = (struct compress_buffer *) xmalloc (sizeof *n); + memset (n, 0, sizeof *n); + + n->buf = buf; + + if (input) + zstatus = inflateInit (&n->zstr); + else + zstatus = deflateInit (&n->zstr, level); + if (zstatus != Z_OK) + compress_error (1, zstatus, &n->zstr, "compression initialization"); + + /* There may already be data buffered on BUF. For an output + buffer, this is OK, because these routines will just use the + buffer routines to append data to the (uncompressed) data + already on BUF. An input buffer expects to handle a single + buffer_data of buffered input to be uncompressed, so that is OK + provided there is only one buffer. At present that is all + there ever will be; if this changes, compress_buffer_input must + be modified to handle multiple input buffers. */ + assert (! input || buf->data == NULL || buf->data->next == NULL); + + return buf_initialize (input ? compress_buffer_input : NULL, + input ? NULL : compress_buffer_output, + input ? NULL : compress_buffer_flush, + compress_buffer_block, + (input + ? compress_buffer_shutdown_input + : compress_buffer_shutdown_output), + memory, + n); +} + +/* Input data from a compression buffer. */ + +static int +compress_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + struct buffer_data *bd; + + if (cb->buf->input == NULL) + abort (); + + /* We use a single buffer_data structure to buffer up data which + the z_stream structure won't use yet. We can safely store this + on cb->buf->data, because we never call the buffer routines on + cb->buf; we only call the buffer input routine, since that + gives us the semantics we want. As noted in + compress_buffer_initialize, the buffer_data structure may + already exist, and hold data which was already read and + buffered before the decompression began. */ + bd = cb->buf->data; + if (bd == NULL) + { + bd = ((struct buffer_data *) malloc (sizeof (struct buffer_data))); + if (bd == NULL) + return -2; + bd->text = (char *) malloc (BUFFER_DATA_SIZE); + if (bd->text == NULL) + { + free (bd); + return -2; + } + bd->bufp = bd->text; + bd->size = 0; + cb->buf->data = bd; + } + + cb->zstr.avail_out = size; + cb->zstr.next_out = (Bytef *) data; + + while (1) + { + int zstatus, sofar, status, nread; + + /* First try to inflate any data we already have buffered up. + This is useful even if we don't have any buffered data, + because there may be data buffered inside the z_stream + structure. */ + + cb->zstr.avail_in = bd->size; + cb->zstr.next_in = (Bytef *) bd->bufp; + + do + { + zstatus = inflate (&cb->zstr, Z_NO_FLUSH); + if (zstatus == Z_STREAM_END) + break; + if (zstatus != Z_OK && zstatus != Z_BUF_ERROR) + { + compress_error (0, zstatus, &cb->zstr, "inflate"); + return EIO; + } + } while (cb->zstr.avail_in > 0 + && cb->zstr.avail_out > 0); + + bd->size = cb->zstr.avail_in; + bd->bufp = (char *) cb->zstr.next_in; + + if (zstatus == Z_STREAM_END) + return -1; + + /* If we have obtained NEED bytes, then return, unless NEED is + zero and we haven't obtained anything at all. If NEED is + zero, we will keep reading from the underlying buffer until + we either can't read anything, or we have managed to + inflate at least one byte. */ + sofar = size - cb->zstr.avail_out; + if (sofar > 0 && sofar >= need) + break; + + /* All our buffered data should have been processed at this + point. */ + assert (bd->size == 0); + + /* This will work well in the server, because this call will + do an unblocked read and fetch all the available data. In + the client, this will read a single byte from the stdio + stream, which will cause us to call inflate once per byte. + It would be more efficient if we could make a call which + would fetch all the available bytes, and at least one byte. */ + + status = (*cb->buf->input) (cb->buf->closure, bd->text, + need > 0 ? 1 : 0, + BUFFER_DATA_SIZE, &nread); + if (status != 0) + return status; + + /* If we didn't read anything, then presumably the buffer is + in nonblocking mode, and we should just get out now with + whatever we've inflated. */ + if (nread == 0) + { + assert (need == 0); + break; + } + + bd->bufp = bd->text; + bd->size = nread; + } + + *got = size - cb->zstr.avail_out; + + return 0; +} + +/* Output data to a compression buffer. */ + +static int +compress_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + + cb->zstr.avail_in = have; + cb->zstr.next_in = (unsigned char *) data; + + while (cb->zstr.avail_in > 0) + { + char buffer[BUFFER_DATA_SIZE]; + int zstatus; + + cb->zstr.avail_out = BUFFER_DATA_SIZE; + cb->zstr.next_out = (unsigned char *) buffer; + + zstatus = deflate (&cb->zstr, Z_NO_FLUSH); + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &cb->zstr, "deflate"); + return EIO; + } + + if (cb->zstr.avail_out != BUFFER_DATA_SIZE) + buf_output (cb->buf, buffer, + BUFFER_DATA_SIZE - cb->zstr.avail_out); + } + + *wrote = have; + + /* We will only be here because buf_send_output was called on the + compression buffer. That means that we should now call + buf_send_output on the underlying buffer. */ + return buf_send_output (cb->buf); +} + +/* Flush a compression buffer. */ + +static int +compress_buffer_flush (closure) + void *closure; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + + cb->zstr.avail_in = 0; + cb->zstr.next_in = NULL; + + while (1) + { + char buffer[BUFFER_DATA_SIZE]; + int zstatus; + + cb->zstr.avail_out = BUFFER_DATA_SIZE; + cb->zstr.next_out = (unsigned char *) buffer; + + zstatus = deflate (&cb->zstr, Z_SYNC_FLUSH); + + /* The deflate function will return Z_BUF_ERROR if it can't do + anything, which in this case means that all data has been + flushed. */ + if (zstatus == Z_BUF_ERROR) + break; + + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &cb->zstr, "deflate flush"); + return EIO; + } + + if (cb->zstr.avail_out != BUFFER_DATA_SIZE) + buf_output (cb->buf, buffer, + BUFFER_DATA_SIZE - cb->zstr.avail_out); + + /* If the deflate function did not fill the output buffer, + then all data has been flushed. */ + if (cb->zstr.avail_out > 0) + break; + } + + /* Now flush the underlying buffer. Note that if the original + call to buf_flush passed 1 for the BLOCK argument, then the + buffer will already have been set into blocking mode, so we + should always pass 0 here. */ + return buf_flush (cb->buf, 0); +} + +/* The block routine for a compression buffer. */ + +static int +compress_buffer_block (closure, block) + void *closure; + int block; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + + if (block) + return set_block (cb->buf); + else + return set_nonblock (cb->buf); +} + +/* Shut down an input buffer. */ + +static int +compress_buffer_shutdown_input (closure) + void *closure; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + int zstatus; + + /* Pick up any trailing data, such as the checksum. */ + while (1) + { + int status, nread; + char buf[100]; + + status = compress_buffer_input (cb, buf, 0, sizeof buf, &nread); + if (status == -1) + break; + if (status != 0) + return status; + } + + zstatus = inflateEnd (&cb->zstr); + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &cb->zstr, "inflateEnd"); + return EIO; + } + + return buf_shutdown (cb->buf); +} + +/* Shut down an output buffer. */ + +static int +compress_buffer_shutdown_output (closure) + void *closure; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + int zstatus, status; + + do + { + char buffer[BUFFER_DATA_SIZE]; + + cb->zstr.avail_out = BUFFER_DATA_SIZE; + cb->zstr.next_out = (unsigned char *) buffer; + + zstatus = deflate (&cb->zstr, Z_FINISH); + if (zstatus != Z_OK && zstatus != Z_STREAM_END) + { + compress_error (0, zstatus, &cb->zstr, "deflate finish"); + return EIO; + } + + if (cb->zstr.avail_out != BUFFER_DATA_SIZE) + buf_output (cb->buf, buffer, + BUFFER_DATA_SIZE - cb->zstr.avail_out); + } while (zstatus != Z_STREAM_END); + + zstatus = deflateEnd (&cb->zstr); + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &cb->zstr, "deflateEnd"); + return EIO; + } + + status = buf_flush (cb->buf, 1); + if (status != 0) + return status; + + return buf_shutdown (cb->buf); +} + +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ diff --git a/contrib/cvs/tools/ChangeLog b/contrib/cvs/tools/ChangeLog index c6b6e1c7d..407c053 100644 --- a/contrib/cvs/tools/ChangeLog +++ b/contrib/cvs/tools/ChangeLog @@ -1,3 +1,16 @@ +Wed Jan 8 14:50:47 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in: Remove CVSid; we decided to get rid + of these some time ago. + +Thu Jan 2 13:30:56 1997 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in: Remove "675" paragraph; see ../ChangeLog for rationale. + +Fri Aug 16 16:05:56 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * Makefile.in (installdirs): new (empty) target + Sun Apr 14 11:07:43 1996 Karl Fogel <kfogel@floss.red-bean.com> * .cvsignore: new file. diff --git a/contrib/cvs/tools/Makefile.in b/contrib/cvs/tools/Makefile.in index c83d1a8..3a39bc3 100644 --- a/contrib/cvs/tools/Makefile.in +++ b/contrib/cvs/tools/Makefile.in @@ -12,12 +12,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# $CVSid: @(#)Makefile.in 1.6 94/10/22 $ - SHELL = /bin/sh srcdir = @srcdir@ @@ -42,6 +36,9 @@ install: all @echo "pcl-cvs not installed" .PHONY: install +installdirs: +.PHONY: installdirs + tags: .PHONY: tags diff --git a/contrib/cvs/tools/pcl-cvs/ChangeLog b/contrib/cvs/tools/pcl-cvs/ChangeLog index 0192b93..a9e6f47 100644 --- a/contrib/cvs/tools/pcl-cvs/ChangeLog +++ b/contrib/cvs/tools/pcl-cvs/ChangeLog @@ -1,3 +1,29 @@ +Mon Oct 14 19:19:17 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * pcl-cvs.el (cvs-parse-stderr): Ignore messages about patch hunks + failing; CVS will refetch the file. + +Thu Oct 10 10:20:20 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * Makefile.in: Allow pcl-cvs to find the site-lisp directory where + it is installed in modern Emacsen. (Patch thanks to David Kågedal.) + +Wed Sep 25 05:38:09 1996 Jim Blandy <jimb@floss.cyclic.com> + + * pcl-cvs.el (cvs-find-program): New function. + (cvs-program, cvs-diff-program): Use it to find the executables. + (local-path, local-gnu-path): Variables deleted; nobody else was + using them, and they didn't do the job right anyway. + +Wed Sep 11 15:43:50 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * pcl-cvs.el (cvs-parse-stderr): Ignore messages about obtained locks. + +Fri Aug 9 09:44:53 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * pcl-cvs.el (cvs-parse-stderr): Remove extra paren from regexp + matching rcsmerge warnings. + Mon Apr 15 01:34:27 1996 Karl Fogel <kfogel@floss.red-bean.com> * Makefile: removed. Why was this in the repository in the first diff --git a/contrib/cvs/tools/pcl-cvs/ChangeLog.woods b/contrib/cvs/tools/pcl-cvs/ChangeLog.woods new file mode 100644 index 0000000..827c3f2 --- /dev/null +++ b/contrib/cvs/tools/pcl-cvs/ChangeLog.woods @@ -0,0 +1,383 @@ +Thu Jan 4 13:19:20 1996 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - from Cyclic CVS version: + + Sat Dec 30 15:01:45 1995 Karl Fogel <kfogel@floss.cyclic.com> + + * pcl-cvs.el (cvs-changelog-ours-p): check that + `add-log-full-name' and `add-log-mailing-address' are non-nil, in + addition to checking that they are boundp. + +Fri Dec 22 17:52:17 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - (cvs-parse-stdout): make the "connection timed out" an error + + - from Cyclic CVS version: + + Thu Dec 21 16:45:48 1995 Karl Fogel <kfogel@occs.cs.oberlin.edu> + + * pcl-cvs.el (cvs-parse-stderr): ignore kerberos connection + failure, since CVS will automatically try rsh next. I think this + is okay because if a person needs to know that kerberos failed, + then chances are the rsh failed too, and *that* error message will + clue them in that something's afoot. + +Thu Dec 21 21:13:10 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - (cvs-parse-stdout): handle "Permission denied" (which will + often precede the "premature EOF from server" message....) + + - from Cyclic CVS version: + + Wed Nov 22 11:01:50 1995 Joshua Cowan <jcowan@hermit.reslife.okstate.edu> + + * pcl-cvs.el (cvs-changelog-ours-p): use `user-full-name' if + `add-log-full-name' unbound, as not every uses the stuff in + add-log.el. Same with `add-log-mailing-address'. + (cvs-changelog-entries): change to `change-log-mode' unless + already in it. + + * ToDo: + - more new ideas.... + +Mon Nov 27 23:19:50 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - add more support for recognizing unknown directories + - add initial support for properly supporting directories with cvs-mode-add + +Tue Nov 21 16:51:45 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - fix up the debug mail message buffer formatting + - include bugs@most.weird.com on debug message list (i.e. my gnats) + (cvs-parse-stderr): fix up handling of some transient errors, such as + network server errors + +Mon Nov 20 19:13:34 1995 Greg A. Woods <woods@most.weird.com> + + * Makefile: + - minor tweaks on version numbers, etc. + - added '-n -q' to emacs arguments... + + * pcl-cvs.texinfo: + - mention new pcl-cvs release properly + - mention new CVS release + - fix grammar & layout style here and there... + - move GPL to the bottom and into an included file (should ref another + info document) + - add Ediff documentation, fix up Emerge docs. + - updated Contributors section + - add note about 'Patched' state (FIXME: more remote docs needed!!!!) + - added some documentation for cvs-mode-changelog-commit + - added a few more index entries, etc. + + * pcl-cvs.el: + - update comments and documentation, fix grammar, etc. + - mention new CVS release + - re-sort cvs-mode-map setup, document key-map convention + - re-write (cvs-mode-ediff), adding (cvs-old-ediff-interface) + - fix (cvs-retrieve-revision-to-tmpfile) to handle empty revision arg. + + * ToDo: + - lots of new ideas + - give up on 'C-u' for cvs-mode-* + - minor prioritisation + +Thu Nov 16 20:38:58 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el (cvs-mode-emerge): + fix the quit-hook setup for ediff-* + (cvs-mode-ediff): new, based on cvs-mode-emerge + + * pcl-cvs.el (cvs-mode-emerge): + cleanups and use emerge-hooks, and protect from + multiple invocations (idea from Loren James Rittle <rittle@comm.mot.com>) + + * pcl-cvs.el (cvs-diff-program): + document problems with vendor versions + + * pcl-cvs.el, ToDo: + - add minimal "cvs tag" and "cvs rtag" support + (cvs-mod): clean up the documentation + (cvs-mode-map): clean up a bit too + (cvs-mode-log), (cvs-mode-status): clean up implementation + +Wed Nov 15 18:39:22 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el, ToDo: + (cvs-parse-stderr) - handle some more error messages from CVS + + * pcl-cvs.el: + Various tweaks and fixes after diffing with CVS-1.6.1 version: + - spelling/typos + - minor re-indentation + - missed one check for PATCHED + - more documentation and comment fixups + + * pcl-cvs.el: + From: Loren James Rittle <rittle@comm.mot.com> + To: info-cvs@prep.ai.mit.edu + Subject: Change to allow pcl-cvs to use ediff instead of emerge + Date: Mon, 6 Nov 95 17:17:31 CST + + 2) ensures the user wants to `update' in cases where a possible conflict + currectly exists; + + * pcl-cvs.el: + Message-Id: <9511062119.AA29213@supra.comm.mot.com> + From: Loren James Rittle <rittle@comm.mot.com> + To: info-cvs@prep.ai.mit.edu + Subject: Change to allow pcl-cvs to use more hidden buffer names + Date: Mon, 6 Nov 95 15:19:57 CST + + + PCL-CVS uses hidden buffers in some places. This patch makes more + PCL-CVS buffers hidden. + + Loren + +Tue Nov 14 20:56:41 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el (cvs-do-removal): + don't fail if the file is missing or a directory + (cvs-mode-add-change-log-entry-other-window): get the + change-log-filename and entry name right + + * pcl-cvs.el, ChangeLog: + * pcl-cvs.el (local-path, local-gnu-path): added documentation + string + (cvs-rmdir-program): use the proper default and leave it alone + + * pcl-cvs.el: + - from ccvs official sources: + revision 1.12 + pcl-cvs.el 1995/05/19 01:59:26 jimb + Automatically guess CVS log entries from ChangeLog contents. + * pcl-cvs.el (cvs-mode-changelog-commit): New command. + (cvs-changelog-full-paragraphs): New variable. + (cvs-changelog-name, cvs-narrow-changelog, + cvs-changelog-paragraph, cvs-changelog-subparagraph, + cvs-changelog-entry, cvs-changelog-ours-p, cvs-relative-path, + cvs-changelog-entries, cvs-changelog-insert-entries, cvs-union, + cvs-insert-changelog-entries, cvs-edit-delete-common-indentation): New functions. + (cvs-mode-map): Bind 'C' to cvs-mode-changelog-commit. + (cvs-mode): Mention cvs-mode-changelog-commit in docstring. + + * ChangeLog: + - from ccvs official sources: + revision 1.12 + ChangeLog 1995/05/19 01:59:26 jimb + Automatically guess CVS log entries from ChangeLog contents. + * pcl-cvs.el (cvs-mode-changelog-commit): New command. + (cvs-changelog-full-paragraphs): New variable. + (cvs-changelog-name, cvs-narrow-changelog, + cvs-changelog-paragraph, cvs-changelog-subparagraph, + cvs-changelog-entry, cvs-changelog-ours-p, cvs-relative-path, + cvs-changelog-entries, cvs-changelog-insert-entries, cvs-union, + cvs-insert-changelog-entries, cvs-edit-delete-common-indentation): New functions. + (cvs-mode-map): Bind 'C' to cvs-mode-changelog-commit. + (cvs-mode): Mention cvs-mode-changelog-commit in docstring. + + * pcl-cvs.el: + - slight variant of this change ccvs official sources + revision 1.10 + pcl-cvs.el 1995/05/03 18:56:20 jimb + (cvs-parse-stderr): Handle colons after both "rcsmerge" and "warning". + + * pcl-cvs.el: + - from ccvs official sources + revision 1.9 + pcl-cvs.el 1995/04/26 02:36:15 jimb + (cvs-parse-stderr): Recognize "conflicts" as well as "overlaps" before + "during merge." + - also fixed a few other minor inconsitencies in that region... + + * pcl-cvs.el: + - from ccvs official sources + revision 1.5 + pcl-cvs.el 1995/01/31 23:31:39 jimb + (cvs-cvsroot-required): New variable. + (cvs-do-update): If cvs-cvsroot-required is not set, don't complain if + CVSROOT and cvs-cvsroot are both unset. + + * pcl-cvs.el: + - use 'identity instead of '(lambda (foo) foo) + - fix a botch in cvs-make-list + - check cvs-status-flags and cvs-log-flags are lists before using them + +Mon Nov 13 23:49:25 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - fixed up version strings + - minor changes to cvs-*-flags defaults + - added functions parse-string and cvs-make-list + - modified various parsers to handle "cvs server:" messages + - added support for "P" flag (PATCHED) + - fixed use of "-Q" for "cvs status" + - use cvs-make-list when reading new values for cvs-*-flags + + * Makefile: + - fix the info install rules... + + * ToDo: + - more things to look into... + +Sun Mar 12 20:40:05 1995 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - minor twiggle to old RCS Id's to match format + + * Makefile: + - fix up RCS Id's + - add clobber to "clean" targets + - separate installation of *.el's and default off + - fix INSTALL_DATA + + * pcl-cvs.el: + - minor twiggles to automatically localise and to note this is a local version + + * ToDo: + - first cut + + * .cvsignore: + - first time in -- default generated targets + +Tue Nov 22 20:49:39 1994 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - adjust RCS keywords + - reset shell to just /bin/sh + +Fri Nov 4 21:14:55 1994 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - fix directory for new CVS-1.4 + - add '-v' to cvs-status-flags + +Mon Dec 6 19:06:18 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - I don't quite know how that happened, but we had a variable and + function with the same name, and an un-defined variable.... + + * pcl-cvs.el: + - re-format a few things.... + - replace numerous duplicate lambda expressions with a new common + function: cvs-quote-multiword-string + +Fri Nov 26 20:54:25 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - oops! was changing the directory string we were comparing against! + + * pcl-cvs.el: + - oops -- missing an end-sexp.... + + * pcl-cvs.el: + - in theory this fixes up all the local directory vs. + default-directory problems + + * pcl-cvs.el: + - ensure multi-word args are quoted in messages + - enhance naming of diff buffers + +Fri Nov 12 18:43:09 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - update comment about "Conflict"... + - fiddle with rcs-ident stuff + +Thu Nov 4 01:15:53 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - set default cvs-status-flags to '("-Q) + - use cvs-bakprefix where intended + - add functions and keymaps for setting cvs-*-flags. + - re-pretty-print some stuff + +Wed Nov 3 23:11:15 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - use cvs-kill-buffer-visiting in cvs-mode-diff-vendor for cleanup. + - fix last minibuffer message in cvs-mode-diff-vendor. + + * pcl-cvs.el: + - OOPS! put (list ...) back where we need evaluation. + - default to use of CVS/Root always for cvs-update, if it exits. + + * pcl-cvs.el: + - oops -- minor bug with usage of mapconcat + - changed (list ...) to '(...) + + * pcl-cvs.texinfo: + - minor updates of filenames and versions. + - added myself as a contributor + + * pcl-cvs.el: + - re-justified multitueds of comments, and fixed minor bits too. + - added support for CVS/Root file to supplement use of $CVSROOT and/or + cvs-cvsroot. + - added cvs-mode-map binding for cvs-change-cvsroot + - added optional message-fmt argument to cvs-execute-list (prints + message showing progress in minibuffer). + - modified cvs-mode-diff-cvs to put results in + cvs-fileinfo->cvs-diff-buffer. + - modified cvs-mode-diff-backup to put results in + cvs-fileinfo->backup-diff-buffer. + - modified cvs-mode-diff-vendor to put results in + cvs-fileinfo->vendor-diff-buffer. + - modified cvs-mode-diff-* to use cvs-diff-flags. + - added cvs-*-diffable. + - fixed various minibuffer progress messages. + +Thu Oct 28 23:10:38 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + - many minor improvements, + - several bug fixes, + - added some support for vendor branch merging, + +Tue Oct 26 18:26:33 1993 Greg A. Woods <woods@most.weird.com> + + * Makefile: + - fixes, localisations, etc. + +Fri Oct 22 21:19:15 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el, Makefile: + - localisations.... + + * pcl-cvs.el: + pick the 1.05 version over local edits.... + +Thu Oct 21 21:40:26 1993 Greg A. Woods <woods@most.weird.com> + + * texinfo.tex, pcl-cvs.texinfo, pcl-cvs.el, pcl-cvs-startup.el, pcl-cvs-lucid.el, compile-all.el, README, NEWS, Makefile, INSTALL, ChangeLog: + pcl-cvs version 1.05 distribution + +Tue Jun 1 16:25:35 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + add a newline to the cvs-mode-add description + +Fri May 28 14:13:41 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + add cvs-commit-buffer-require-final-newline to allow forcing a newline + onto the commit buffer + +Fri May 14 22:35:01 1993 Greg A. Woods <woods@most.weird.com> + + * pcl-cvs.el: + localizations.... + + * ChangeLog, INSTALL, Makefile, NEWS, README, compile-all.el, pcl-cvs.el, pcl-cvs-lucid.el, pcl-cvs-startup.el, pcl-cvs.texinfo, texinfo.tex: + pcl-cvs version 1.04 distribution + + * ChangeLog, INSTALL, Makefile, NEWS, README, compile-all.el, pcl-cvs.el, pcl-cvs-lucid.el, pcl-cvs-startup.el, pcl-cvs.texinfo, texinfo.tex: + Initial revision + diff --git a/contrib/cvs/tools/pcl-cvs/Makefile.in b/contrib/cvs/tools/pcl-cvs/Makefile.in index ca881d0..23e47ee 100644 --- a/contrib/cvs/tools/pcl-cvs/Makefile.in +++ b/contrib/cvs/tools/pcl-cvs/Makefile.in @@ -4,7 +4,7 @@ # #ident "@(#)original: dist-makefile,v 1.19 1993/05/31 22:43:45 ceder Exp " # -#ident "@(#)elisp/pcl-cvs:$Name: $:$Id: Makefile.in,v 1.3 1996/04/15 06:33:20 kfogel Exp $" +#ident "@(#)elisp/pcl-cvs:$Name: $:$Id: Makefile.in,v 1.6 1997/02/17 20:44:30 kingdon Exp $" # # Makefile for pcl-cvs release 1.05-CVS-$Name: $. # Copyright (C) 1992, 1993 Per Cederqvist @@ -35,7 +35,7 @@ prefix = @prefix@ exec_prefix = @exec_prefix@ # Where to put the system-wide supplementary files -libdir = $(prefix)/lib +sharedir = $(prefix)/share # Where to put the Info files infodir = $(prefix)/info @@ -49,7 +49,7 @@ EMACS = emacs BATCHFLAGS = -batch # This is the directory in which the ELCFILES will be installed. -lispdir = $(libdir)/emacs/site-lisp +lispdir = $(sharedir)/emacs/site-lisp #### End of system configuration section. #### diff --git a/contrib/cvs/tools/pcl-cvs/ToDo b/contrib/cvs/tools/pcl-cvs/ToDo new file mode 100644 index 0000000..04eb557 --- /dev/null +++ b/contrib/cvs/tools/pcl-cvs/ToDo @@ -0,0 +1,44 @@ +- documentation.... (esp. ChangeLog support) + +- IMPORTANT: make cvs-mode-tag run in the background + +- fix 'cvs-mode-add' to "cvs update" in the added directory + +- write 'cvs-mode-import' + +- write 'cvs-mode-handle-new-vendor-version' + + - checks out module + - does "cvs -n tag LAST_VENDOR" to find old files into *cvs* + +- add 'cvs-set-cvs-flags' for top level cvs command flags + +- add '\M-a' to do arbitrary 'cvs admin' commands + +- add 'cvs patch' support (with completion on tag names and hooks to +help generate ChangeLog files with rcs2log, etc.) + +- add 'cvs export' support (with completion on tag names and hooks to +help generate full releases) + +- add 'cvs-mode-version' to call 'cvs -v' [on ^C-cv ?] (really???) + +- re-write rcs2log in e-lisp. + +- add support for parsing 'modules' file ("cvs co -c") + +- enhance 'cvs-mode-rtag' + +- fix 'cvs-examine' + +- write 'cvs-mode-checkout' and 'cvs-mode-checkout-other-window' + +- write 'cvs-mode-release' + +- add an update mode that keeps all files + + - this will require all commands to work only on non-up-to-date + files + +-- +#ident "@(#)cvs/contrib/pcl-cvs:$Name: $Id$" diff --git a/contrib/cvs/tools/pcl-cvs/pcl-cvs.el b/contrib/cvs/tools/pcl-cvs/pcl-cvs.el index 269b02f..8a13122 100644 --- a/contrib/cvs/tools/pcl-cvs/pcl-cvs.el +++ b/contrib/cvs/tools/pcl-cvs/pcl-cvs.el @@ -1,7 +1,7 @@ ;;; ;;;#ident "@(#)OrigId: pcl-cvs.el,v 1.93 1993/05/31 22:44:00 ceder Exp " ;;; -;;;#ident "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: pcl-cvs.el,v 1.2 1996/04/14 20:09:45 kfogel Exp $" +;;;#ident "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: pcl-cvs.el,v 1.6 1996/11/06 17:29:31 jimb Exp $" ;;; ;;; pcl-cvs.el -- A Front-end to CVS 1.3 or later. ;;; Release 1.05-CVS-$Name: $. @@ -48,30 +48,27 @@ ;; also use $GNU here, since may folks might install CVS as a GNU package ;; -(defvar local-path (cond - ((getenv "LOCAL") - (getenv "LOCAL")) - ((getenv "GNU") - (getenv "GNU")) - (t - "/usr/local")) - "*Path prefix for most locally installed things.") - -;; this isn't likely to be right all the time.... -;; -(defvar local-gnu-path (cond - ((getenv "GNU") - (getenv "GNU")) - (t - "/usr/local")) ; or "/usr/gnu"? - "*Path prefix for locally installed GNU software.") - -(defvar cvs-program (concat local-path "/bin/cvs") +(defun cvs-find-program (program) + (let ((path (list (getenv "LOCAL") + (getenv "GNU") + "/usr/local/bin" + "/usr/bin" + "/bin"))) + (while path + (if (stringp (car path)) + (let ((abs-program (expand-file-name program (car path)))) + (if (file-executable-p abs-program) + (setq path nil + program abs-program)))) + (setq path (cdr path))) + program)) + +(defvar cvs-program (cvs-find-program "cvs") "*Full path to the cvs executable.") ;; SunOS-4.1.1_U1 has "diff.c 1.12 88/08/04 SMI; from UCB 4.6 86/04/03" ;; -(defvar cvs-diff-program (concat local-gnu-path "/bin/diff") +(defvar cvs-diff-program (cvs-find-program "diff") "*Full path to the best diff program you've got. NOTE: there are some nasty bugs in the context diff variants of some vendor versions, such as the one in SunOS-4.1.1_U1") @@ -1017,7 +1014,7 @@ ERR-BUF should be 'STDOUT or 'STDERR." (insert "Pcl-cvs Version: " "@(#)OrigId: pcl-cvs.el,v 1.93 1993/05/31 22:44:00 ceder Exp\n") (insert "CVS Version: " - "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: pcl-cvs.el,v 1.2 1996/04/14 20:09:45 kfogel Exp $\n\n") + "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: pcl-cvs.el,v 1.6 1996/11/06 17:29:31 jimb Exp $\n\n") (insert (format "--- Contents of stdout buffer (%d chars) ---\n" (length stdout))) (insert stdout) @@ -1179,6 +1176,11 @@ This function returns the last cons-cell in the list that is built." "^cvs \\(update\\|server\\): warning: .* was lost$") (forward-line 1)) + ;; Patch failed; CVS will refetch the file. Ignored. + ((looking-at + "^[0-9]+ out of [0-9]+ hunks failed--saving rejects to .*$") + (forward-line 1)) + ;; File unknown for some reason. ;; FIXME: is it really a good idea to add this as unknown here? @@ -1223,6 +1225,9 @@ This function returns the last cons-cell in the list that is built." ((looking-at "^cvs \\(update\\|server\\): \\[..:..:..\\] waiting for .*lock in ") (forward-line 1)) + ((looking-at + "^cvs \\(update\\|server\\): \\[..:..:..\\] obtained lock in ") + (forward-line 1)) ;; File removed in repository, but edited by you. @@ -1344,7 +1349,7 @@ This function returns the last cons-cell in the list that is built." ((looking-at ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning"). - "^\\(rcs\\)?merge[:]*\\( warning\\)?: \\((overlaps\\|conflicts\\) during merge$") + "^\\(rcs\\)?merge[:]*\\( warning\\)?: \\(overlaps\\|conflicts\\) during merge$") ;; Yes, this is a conflict. (cvs-skip-line stdout-buffer stderr-buffer |