From: Matthew Dillon Date: Tue, 3 Aug 2004 18:08:51 +0000 (+0000) Subject: Bring cvs-1.12.9 into the CVS repository X-Git-Tag: v2.0.1~10571^2 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/5182484f66d4430380ceb6dcc50b13c4eecd5152 Bring cvs-1.12.9 into the CVS repository --- 5182484f66d4430380ceb6dcc50b13c4eecd5152 diff --git a/contrib/cvs-1.12.9/ABOUT-NLS b/contrib/cvs-1.12.9/ABOUT-NLS new file mode 100644 index 0000000000..da27fadb97 --- /dev/null +++ b/contrib/cvs-1.12.9/ABOUT-NLS @@ -0,0 +1,5 @@ +Automake started requiring this file after I included the gettext support from +GNULIB . I'm not sure why, exactly, +but there was an nls.m4 macro I had to import which claims that NLS stands for +"Native Language Support". If someone would like to replace this ABOUT-NLS +file with a more appropriate one, please do. diff --git a/contrib/cvs-1.12.9/AUTHORS b/contrib/cvs-1.12.9/AUTHORS new file mode 100644 index 0000000000..300b6dc921 --- /dev/null +++ b/contrib/cvs-1.12.9/AUTHORS @@ -0,0 +1,88 @@ +Authors of GNU CVS + +The conflict-resolution algorithms and much of the administrative file +definitions of CVS were based on the original package written by Dick Grune +at Vrije Universiteit in Amsterdam , and posted to +comp.sources.unix in the volume 6 release sometime in 1986. This original +version was a collection of shell scripts. I am thankful that Dick made +his work available. + +Brian Berliner from Prisma, Inc. (now at Sun Microsystems, Inc.) + converted the original CVS shell scripts into reasonably +fast C and added many, many features to support software release control +functions. See the manual page in the "man" directory. A copy of the +USENIX article presented at the Winter 1990 USENIX Conference, Washington +D.C., is included in the "doc" directory. + +Jeff Polk from BSDI 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 bottom of the NEWS file (from about 1992). + +david d `zoo' zuhn contributed the working base code +for CVS 1.4 Alpha. His work carries on from work done by K. Richard Pixley +and others at Cygnus Support. The CVS 1.4 upgrade is due in large part to +Zoo's efforts. + +David G. Grubbs contributed the CVS "history" and "release" +commands. As well as the ever-so-useful "-n" option of CVS which tells CVS +to show what it would do, without actually doing it. He also contributed +support for the .cvsignore file. + +The Free Software Foundation (GNU) contributed most of the portability +framework that CVS now uses. This can be found in the "configure" script, +the Makefile's, and basically most of the "lib" directory. + +K. Richard Pixley, Cygnus Support contributed many bug +fixes/enhancement as well as completing early reviews of the CVS 1.3 manual +pages. + +Roland Pesch, then of Cygnus Support 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. wrote and +contributed the code in lib/sighandle.c. I added support for POSIX, BSD, +and non-POSIX/non-BSD systems. + +Jim Kingdon and others at Cygnus Support wrote the +remote repository access code. + +Larry Jones and Derek Price have been maintaining and +enhancing CVS for some years. Mark D. Baushke came on in +2003. + +There have been many, many contributions not listed here. Consult the +individual 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 in the Cederqvist manual). + + Mark D. Baushke + Per Cederqvist + J.T. Conklin + Vince DeMarco + Paul Eggert + Lal George + Dean E. Hardi + Mike Heath + Jim Kingdon + Bernd Leibing + Benedict Lofstedt + Dave Love + Robert Lupton the Good + Tom McAliney + Eberhard Mattes + Jim Meyering + Thomas Mohr + Thomas Nilsson + Raye Raskin + Harlan Stenn + Gunnar Tornblom + Greg A. Woods + +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 tools/pcl-cvs. diff --git a/contrib/cvs-1.12.9/BUGS b/contrib/cvs-1.12.9/BUGS new file mode 100644 index 0000000000..5d9b3e3dd8 --- /dev/null +++ b/contrib/cvs-1.12.9/BUGS @@ -0,0 +1,122 @@ +See the Cederqvist manual (cvs.texinfo) for information on how to +report bugs (and what will happen to your bug reports if you do). + +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 the Cederqvist +manual). There are some other reported bugs in MINOR-BUGS; the +difference, at least in theory, is that those bugs are less serious. + + +* 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. + + +* If your login name contains a space or various other characters +(particularly an issue on Windows), CVS will have trouble (it will +write invalid RCS files, probably). The fix would be to have CVS +change such characters to underscores before writing them to the RCS +file. Furthermore, the LOGNAME or USER environment variables usually +won't override the system login name, so this can be hard to work +around. + + +* If you specify the -w global option to client/server CVS, it only +overrides a CVSREAD environment variable set on the client, not a +CVSREAD variable which was set on the server (for example, in .bashrc +when the server was run via rsh). The fix of course will be to +provide a "Option-read-write" request which sends -w, in addition to +"Global_option -r" which sends -r. + + +* Symbolic links to files will not work with or without LockDir. In the +repository, you should avoid using symbolic links to files since this issue +can cause data loss. Symlinks are only a problem when writing files. If your +repository does not allow any write access, symlinks are not a problem. + + +* Symbolic links to directories will not work with LockDir. In the +repository, you should avoid using symbolic links to directories if +you intend to use LockDir as the correct directory will NOT be locked +by CVS during write. Directory symlinks are not recommended, but should work +as long as LockDir is not being used. Symlinks are only a problem when +writing files. If your repository does not allow any write access, symlinks +are never a problem, whether or not LockDir is in use. + + +* 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. + + +* From: "Charles M. Hannum" + 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? + + +* CVS does not always seem to be waiting to the next filesystem timestamp +quanta after commits. So far this has only shown up in testing under the BSDI +OS. The symptoms are that ocassionally CVS will not notice that modified files +are modified, though the file must be modified within a short time after the +commit, probably milliseconds or seconds, for this symptom to be noticed. One +suspected cause is that one of the calls to sleep_past() is being called with +an incorrect value, though this does not explain why symptoms have only been +noticed under BSDI. + + +* Spaces in arguments to `cvs diff' are currently split on spaces and tabs +before being passed to diff. This can often cause diff to abort since it can +no longer interpret its options string and if it can, coincidentally, +interpret its option string, then the problem may be output in unexpected +formats. + + +* `release' of a project subdir does not remove the `subdir' entry from + `./CVS/Entries'. + + +* CVS currently breaks at an assertion failure when `cvs ls ' is used + to list information for a single file. + +* Most of the remote commands are encountering assertion failures when listing + the toplevel of the repository (e.g. `cvs rlog .'). This appears to be + related to the symlinked CVS root fix. + + +* Status + + This experimental version of CVS contains new features which may not have + been tested as thoroughly as the stable release. It is classified as: + + /*-------------. + | Experimental | + `-------------*/ + + /*-------------------------. + | Sane for full scale use. | + `-------------------------*/ + diff --git a/contrib/cvs-1.12.9/COPYING b/contrib/cvs-1.12.9/COPYING new file mode 100644 index 0000000000..57da8a45c3 --- /dev/null +++ b/contrib/cvs-1.12.9/COPYING @@ -0,0 +1,251 @@ +[I have snipped the snail mail address of the FSF because it has +changed in the past and is likely to change again. The current +address should be at http://www.gnu.org/] + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our 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. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, 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 a 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 tell them 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. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement 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 work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. 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 +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual 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 General + Public License. + + d) 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. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying 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. + + 6. 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. + + 7. 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 the 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 +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. 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. + + NO WARRANTY + + 9. 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. + + 10. 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 OF TERMS AND CONDITIONS + + Appendix: 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 humanity, 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. + + + Copyright (C) 19yy + + 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 1, 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. + +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: + + Gnomovision version 69, Copyright (C) 19xx 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. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `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 a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/contrib/cvs-1.12.9/COPYING.LIB b/contrib/cvs-1.12.9/COPYING.LIB new file mode 100644 index 0000000000..5e5cda218c --- /dev/null +++ b/contrib/cvs-1.12.9/COPYING.LIB @@ -0,0 +1,484 @@ +[I have snipped the snail mail address of the FSF because it has +changed in the past and is likely to change again. The current +address should be at http://www.gnu.org/] + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 this library; if not, write to the Free + Software Foundation, Inc. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/contrib/cvs-1.12.9/DEVEL-CVS b/contrib/cvs-1.12.9/DEVEL-CVS new file mode 100644 index 0000000000..464003c853 --- /dev/null +++ b/contrib/cvs-1.12.9/DEVEL-CVS @@ -0,0 +1,61 @@ + 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 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 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, the person will be made a developer. + +---------------------------------------------------------------------- +Policy regarding checkout-only access: + +Checkout-only access to the CVS repository is available to all, on an +anonymous basis (no need for registration or other complications). +The exact technical mechanisms by which it is available are not +covered by this policy. diff --git a/contrib/cvs-1.12.9/HACKING b/contrib/cvs-1.12.9/HACKING new file mode 100644 index 0000000000..ec3504390a --- /dev/null +++ b/contrib/cvs-1.12.9/HACKING @@ -0,0 +1,308 @@ +How to write code for CVS + +* Source + +Patches against the development version of CVS are most likely to be accepted: + + $ cvs -d:pserver:anoncvs@cvs.cvshome.org/cvsroot co ccvs + +* Compiler options + +If you are using GCC, you'll want to configure with -Wall, which can +detect many programming errors. This is not the default because it +might cause spurious warnings, but at least on some machines, there +should be no spurious warnings. For example: + + $ CFLAGS="-g -Wall" ./configure + +Configure is not very good at remembering this setting; it will get +wiped out whenever you do a ./config.status --recheck, so you'll need +to use: + + $ CFLAGS="-g -Wall" ./config.status --recheck + +* Indentation style + +CVS mostly uses a consistent indentation style which looks like this: + +void +foo (char *arg, int c) +{ + long aflag; + + if (arg != NULL) + { + bar (arg); + baz (arg); + } + + switch (c) + { + case 'A': + aflag = 1; + break; + } + + printf ("Literal string line 1\n" + "Literal string line 2\n" + "Literal string line 3\n"); +} + +The file cvs-format.el contains settings for emacs and the NEWS file +contains a set of options for the indent program which I haven't tried +but which are correct as far as I know. You will find some code which +does not conform to this indentation style; the plan is to reindent it +as those sections of the code are changed (one function at a time, +perhaps). + +In a submitted patch it is acceptable to refrain from changing the +indentation of large blocks of code to minimize the size of the patch; +the person checking in such a patch should reindent it. + +* Portability + +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. + +CVS is now assuming a freestanding C89 compiler. If you don't have one, you +should find an old release of GCC that did not require a freestanding C89 +compiler to build, build that on your system, build a newer release of GCC +if you wish, then build CVS using GCC as your freestanding C89 compiler. + +A freestanding C89 compiler is guaranteed to support function prototypes, +void *, and assert(). + +The following headers can be assumed and are included from lib/system.h for a +freestanding C89 implementation: , , , . +We are not assuming the other standard headers listed by C89 (hosted headers) +because these four headers are the only headers guaranteed to be shipped with +a C89 compiler (frestanding compiler). We are not currently assuming that the +system the compiler is running on provides the rest of the C89 headers. + +The following C89 hosted headers can be assumed due to their presence in UNIX +version 7 and are included from lib/system.h: , , , +, , , . can also be assumed but +is included via lib/xtime.h via lib/system.h to include some Autoconf magic +which avoids including and on systems that can't handle +both. + +The following C89 headers are also assumed since we believe GCC includes them +even on systems where it is installed as a freestanding compiler when the +system lacks them, despite their not being required: , . +When the system does not lack these headers, they can sometimes not be +standards compatible, but GCC provides a script, `fixincludes', for the purpose +of fixing ANSI conformance problems and we think we can rely on asking users to +either use GCC or run this script to fix conformance problems manually. A +GNULIB developer has made a statement that if this turns out to be a problem, +GNULIB and substitutes could be included in GNULIB, so if +we discover the problem, this should be discussed on . + +A substitute C99 is included from GNULIB for platforms that lack +this header. Please see the comments in the lib/stdbool_.h file for its +limitations. + + can be assumed despite a lack of a presence in even C99, since +it has been around nearly forever and noone has ever complained about our code +assuming its existance. + +CVS has also been assuming for some time. I am unsure of the +rationale. + +A substitute POSIX.2 header and fnmatch() function is provided for +systems that lack them. Similarly for the non-standard header and +alloca() function. Other substitute headers and functions are also provided +when needed. See the lib directory or the srclist.txt file for more +information. + +Please do not use multi-line strings. Despite their common acceptance by many +compilers, they are not part of the ANSI C specification. As of GCC version +3.3, they are no longer supported. See the Indentation Style section above for +an example of a literal string which is not multi-line but which will print +multiple lines. + +* Other style issues + +When composing header files, do not declare function prototypes using the +`extern' storage-class identifier. Under C89, there is no functional +difference between a function declaration with and without `extern', unless the +function is declared `static'. This is NOT the case for global variables. +Global variables declared in header files MUST be declared `extern'. For +example: + +/* Global variables */ +extern int foo; +extern char *bar; + +/* Function declarations */ +int make_foo(void); +char *make_bar(int _foobar); + +* Run-time behaviors + +Use assert() to check "can't happen" conditions internal to CVS. We +realize that there are functions in CVS which instead return NULL or +some such value (thus confusing the meaning of such a returned value), +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. + +Do not use arbitrary limits (such as PATH_MAX) except perhaps when the +operating system or some external interface requires it. We spent a +lot of time getting rid of them, and we don't want to put them back. +If you find any that we missed, please report it as with other bugs. +In most cases such code will create security holes (for example, for +anonymous readonly access via the CVS protocol, or if a WWW cgi script +passes client-supplied arguments to CVS). + +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 to some functions, +you need a static variable, but use a single pointer so that when the function +is fixed to pass along the argument, then the code can easily use that +argument. + +* Coding standards in general + +Generally speaking the GNU coding standards are mostly used by CVS +(but see the exceptions mentioned above, such as indentation style, +and perhaps an exception or two we haven't mentioned). This is the +file standards.text at the GNU FTP sites. + +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). + +* Regenerating Build Files (UNIX) + +On UNIX, if you wish to change the Build files, you will need Autoconf and +Automake. + +Some combinations of Automake and Autoconf versions may break the +CVS build if file timestamps aren't set correctly and people don't +have the same versions the developers do, so the rules to run them +automatically aren't included in the generated Makefiles unless you run +configure with --enable-maintainer-mode. + +The CVS Makefiles and configure script were built using Automake 1.7.9 and +Autoconf 2.58, respectively. + +There is a known bug in Autoconf 2.57 that will prevent the configure +scripts it generates from working on some platforms. Other combinations of +autotool versions may or may not work. If you get other versions to work, +please send a report to . + +* Regenerating Build Files (Windows) + +If for some reason you end up regenerating the *.mak files to submit a patch, +please run windows-NT/fix-msvc-mak.pl to remove the absolute paths from the +generated *.mak files before generating any patches. + +* Writing 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. Is it worth the cost (in terms of complexity or any other +tradeoffs involved)? Are there better solutions? + +If the design is not yet clear (which is true of most features), then +the design is likely to benefit from more work and community input. +Make a list of issues, or write documentation including rationales for +how one would use the feature. Discuss it with coworkers, a +newsgroup, or a mailing list, and see what other people think. +Distribute some experimental patches and see what people think. The +intention is arrive at some kind of rough community consensus before +changing the "official" CVS. Features like zlib, encryption, and +the RCS library have benefitted from this process in the past. + +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. + +* Writing patches (tactics) + +When you first distribute a patch it may be suitable to just put forth +a rough patch, or even just an idea. But before the end of the +process the following should exist: + + - ChangeLog entry (see the GNU coding standards for details). + + - Changes to the NEWS file and cvs.texinfo, if the change is a + user-visible change worth mentioning. + + - Somewhere, a description of what the patch fixes (often in + comments in the code, or maybe the ChangeLog or documentation). + + - Most of the time, a test case (see TESTS). It can be quite + frustrating to fix a bug only to see it reappear later, and adding + the case to the testsuite, where feasible, solves this and other + problems. See the TESTS file for notes on writing new tests. + +If you solve several unrelated problems, it is generally easier to +consider the desirability of the changes if there is a separate patch +for each issue. Use context diffs or unidiffs for patches. + +Include words like "I grant permission to distribute this patch under +the terms of the GNU Public License" with your patch. By sending a +patch to bug-cvs@gnu.org, you implicitly grant this permission. + +Submitting a patch to bug-cvs is the way to reach the people who have +signed up to receive such submissions (including CVS developers), but +there may or may not be much (or any) response. If you want to pursue +the matter further, you are probably best off working with the larger +CVS community. Distribute your patch as widely as desired (mailing +lists, newsgroups, web sites, whatever). Write a web page or other +information describing what the patch is for. It is neither practical +nor desirable for all/most contributions to be distributed through the +"official" (whatever that means) mechanisms of CVS releases and CVS +developers. Now, the "official" mechanisms do try to incorporate +those patches which seem most suitable for widespread usage, together +with test cases and documentation. So if a patch becomes sufficiently +popular in the CVS community, it is likely that one of the CVS +developers will eventually try to do something with it. But dealing +with the CVS developers may be the last step of the process rather +than the first. + +* What is the schedule for the next release? + +There isn't one. That is, upcoming releases are not announced (or +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: + + dev. 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. + 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 dev, cvs, or test-results, send +a message to "-subscribe@ccvs.cvshome.org" or visit +http://ccvs.cvshome.org/servlets/ProjectMailingListList and follow the +instructions there. + +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@gnu.org. + +Other CVS discussions take place on the info-cvs mailing list +(send mail to info-cvs-request@gnu.org to subscribe) or on +the newsgroup comp.software.config-mgmt. diff --git a/contrib/cvs-1.12.9/INSTALL b/contrib/cvs-1.12.9/INSTALL new file mode 100644 index 0000000000..a164b62fc5 --- /dev/null +++ b/contrib/cvs-1.12.9/INSTALL @@ -0,0 +1,483 @@ +First, read the README file. If you're still happy... + +First you need to obtain and install the CVS executables. If you got +a distribution which contains executables, consult the installation +instructions for that distribution. If you got source code, do not +panic. On many platforms building CVS from source code is a +straightforward process requiring no programming knowledge. See the +section BUILDING FROM SOURCE CODE at the end of this file, which +includes a list of platforms which have been tested. + +------------------------------------------------------------------------------- + +1) Take a look at the CVS documentation, if desired. For most + purposes you want doc/cvs.texinfo, also known as _Version Management + with CVS_ by Per Cederqvist et al. Looking at it might be as simple + as "info cvs" but this will depend on your installation; see README + for more details. + + See what CVS can do for you, and if it fits your environment (or can + possibly be made to fit your environment). If things look good, + continue on. Alternately, just give CVS a try first then figure out + what it is good for. + +2) Set the CVSROOT environment variable to where you want to put your + source repository. See the "Setting up the repository" section of + the Cederqvist manual for details, but the quick summary is just to + pick some directory. We'll use /src/master as an example. For + users of a POSIX shell (sh/bash/ksh) on unix, the following + commands can be placed in user's ~/.profile, ~/.bash_profile file; + or in the site-wide /etc/profile: + + CVSROOT=/src/master; export CVSROOT + + For C shell users on unix place the following commands in the + user's ~/.cshrc, ~/.login, or /etc/chsrc file: + + setenv CVSROOT /src/master + + For Windows users, supposing the repository will be in + d:\src\master, place the following line in c:\autoexec.bat. On + Windows 95, autoexec.bat might not already exist. In that case, + just create a new file containing the following line. + + set CVSROOT=:local:d:\src\master + + If these environment variables are not already set in your current + shell, set them now by typing the above line at the command prompt + (or source the login script you just edited). + The instructions for the remaining steps assume that you have set + the CVSROOT environment variable. + +3) Create the master source repository. Again, the details are in + the "Setting up the repository" section of cvs.texinfo; the + one-line summary is: + + $ cvs init + + In this and subsequent examples we use "$" to indicate the command + prompt; do not type the "$". + +4) It might be a good idea to jump right in and put some sources or + documents directly under CVS control. From within the top-level + directory of your source tree, run the following commands: + + $ cvs import -m "test distribution" ccvs CVS_DIST CVS-TEST + + (Those last three items are, respectively, a repository location, a + "vendor tag", and a "release tag". You don't need to understand + them yet, but read the section "Starting new projects" in the + Cederqvist manual for details). + +5) Having done step 4, one should be able to checkout a fresh copy of the + sources you just imported and hack away at the sources with the + following command: + + $ cd + $ cvs checkout ccvs + + This will make the directory "ccvs" in your current directory and + populate it with the appropriate files and directories. + +6) You may wish to customize the various administrative files, in particular + modules. See the Cederqvist manual for details. + +7) Read the NEWS file to see what's new. + +8) Hack away. + +------------------------------------------------------------------------------- + +BUILDING FROM SOURCE CODE + +Tested platforms + +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@gnu.org (doing so in the form of a diff +to this file, or at least exact suggested text, 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.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.9) + Alpha running alpha-dec-osf4.0 (1.10) + DEC Alpha running Digital UNIX v4.0C using gcc 2.7.2.2 (1.9.14) + DEC Alpha running VMS 6.2 (1.8.85 client-only) + Alpha running NetBSD 1.2E (1.10) +Cray: + J90 (CVS 970215 snapshot) + T3E (CVS 970215 snapshot) +HPPA: + HP 9000/710 running HP-UX 8.07A using gcc (about 1.4A2) + HPPA running HP-UX 9 (1.8) + 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) + HPPA running HP-UX 9.05 (1.9) + HPPA running HP-UX 10.01 (1.7) + HPPA running HP-UX 10.20 (1.10.7) + HPPA running HP-UX 11.11 (1.11.13) (footnote 12) + HPPA 2.0 running HP-UX 10.20 (1.10.9) (footnote 13) + NextSTEP 3.3 (1.7) +i386 family: + Solaris 2.4 using gcc (about 1.4A2) + Solaris 2.6 (1.9) + UnixWare v1.1.1 using gcc (about 1.4A2) + Unixware 2.1 (1.8.86) + Unixware 7 (1.9.29) + ISC 4.0.1 (1.8.87) + Linux (kernel 1.2.x) (1.8.86) + Linux (kernel 2.0.x, RedHat 4.2) (1.10) + Linux (kernel 2.0.x, RedHat 5.x) (1.10) + Linux (kernel 2.2.x, RedHat 6.x) (1.10.8) + Linux (kernel 2.2.x, RedHat 7.x) (1.11) + BSDI 4.0 (1.10.7) + 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.0.5 (1.10.2) + Sequent DYNIX/ptx4.0 (1.10 or so) (remove -linet) + Sequent Dynix/PTX 4.1.4 (1.9.20 or so + patches) + Lynx 2.3.0 080695 (1.6.86) (footnote 9) + Windows NT 3.51 (1.8.86 client; 1.8.3 local) + Windows NT 3.51 service pack 4 (1.9) + Windows NT 3.51 service pack 5 (1.9) -- DOES NOT WORK (footnote 11) + Windows NT 4.0 (1.9 client and local) + Windows NT 4.0 (1.11 client and local - build & test, but no test suite) + Windows 95 (1.9 client and local) + QNX (1.9.1 + patches for strippath() and va_list) + OS/2 Version 3 using IBM C/C++ Tools 2.01 (1.8.86 + patches, client) + OS/2 Version 3 using EMX 0.9c (1.9.22, client) + OS/2 Version 3 using Watcom version ? (? - has this been tested?) +m68k: + 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) + NetBSD/mac68k (1.9.28) +m88k: + Data General AViiON running dgux 5.4R2.10 (1.5) + Data General AViiON running dgux 5.4R3.10 (1.7.1) + 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.10) + SGI running Irix 4.0.5H using gcc and cc (about 1.4A2) (footnote 2) + SGI running Irix 5.3 (1.10) + SGI running Irix 6.2 using SGI MIPSpro 6.2 and beta 7.2 compilers (1.9) + SGI running Irix-6.2 (1.9.8) + SGI running IRIX 6.4 (1.10) + SGI running IRIX 6.5 (1.10.7) + 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.8) + IBM RS/6000 running AIX 4.1 (1.9) + IBM RS/6000 running AIX 4.3 (1.10.7) + Lynx 2.3.1 120495 (1.6.86) (footnote 9) + Lynx 2.5 (1.9) (footnote 10) + MkLinux DR3 GENERIC #6 (1.10.5.1) (presumably LinuxPPC too) + Mac OS X Darwin 6.6 Darwin Kernel Version 6.6 (1.11.1p1) + Mac OS X Darwin 5.5 Darwin Kernel Version 5.5 (1.11.6) (footnote 12) + Mac OS X Darwin 5.5 Darwin Kernel Version 5.5 (1.12.1) (footnote 12) +SPARC: + Sun SPARC running SunOS 4.1.x (1.10) + 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 (1.8.87) + Sun SPARC running Solaris 2.5.1 using gcc 2.7.2.2 (1.9.14) + Sun SPARC running Solaris 2.6 (1.10.7) + Sun UltraSPARC running Solaris 2.6 using gcc 2.8.1 (1.10) + NextSTEP 3.3 (1.7) + Sun SPARC running Linux 2.0.17, gcc 2.7.2 (1.8.87) + Sun UltraSPARC running Solaris 2.8 using gcc 2.95.3 +VAX: + VAX running VMS 6.2 (1.9+patches, client-only) + (see README.VMS for information on necessary hacks). + +(footnote 2) + Some Irix 4.0 systems may core dump in malloc while running + CVS. We believe this is a bug in the Irix malloc. You can + workaround this bug by linking with "-lmalloc" if necessary. + (about 1.4A2). + +(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 6) Build in ucb universe with COFF compiler tools. Put + /usr/local/bin first in PATH while doing a configure, make + and install of GNU diffutils-2.7, rcs-5.7, then cvs-1.5. + +(footnote 7) Manoj Srivastava reports + success with this configure command: + CC=cc CFLAGS='-O2 -Olimit 2000 -std1' ./configure --verbose alpha-dec-osf + +(footnote 8) Manoj Srivastava reports + success with this configure command: + CC=cc CFLAGS='+O2 -Aa -D_HPUX_SOURCE' ./configure --verbose hppa1.1-hp-hpux + +(footnote 9) + Had to configure with ./configure --host=-lynx. + + In src/cvs.h, protected the waitpid prototype with ifdef _POSIX_SOURCE. + (I might try building with gcc -mposix -D_POSIX_SOURCE.) + + LynxOS has , but you don't want to use it. + You want to use instead. + So after running configure I had to undef HAVE_DIRENT_H and + define HAVE_SYS_DIR_H. + +(footnote 10) + Had to compile with "make LIBS=-lbsd" (to get gethostbyname + and getservbyname). + +(footnote 11) + when I do a `cvs init' I get this message: + ci: 'RCS/loginfo,v' is not a regular file + ci: RCS/loginfo,v: Invalid argument + cvs [init aborted]: failed to checkin n:/safe/CVSROOT/loginfo + +(footnote 12) + Need to `configure --without-gssapi' unless you have installed Kerberos 5 + libraries on the system yourself. For some reason Apple ships OS X with + the Kerberos 5 headers installed and not the libraries, which confuses the + current configure script. Some HP, BSD, & Sun boxes have similar problems. + +(footnote 13) + A build under HP PA-RISC 2.0 will probably not run under PA-RISC 1.1 + unless "+DAportable" is added to the HP ANSI cc compiler flags. + +------------------------------------------------------------------------------- + +Building from source code under Unix: + +1) Run "configure": + + $ ./configure + + You can specify an alternate destination to override the default with + the --prefix option: + + $ ./configure --prefix=/usr/local/gnu + + or some path that is more appropriate for your site. The default prefix + 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 building CVS with the server enabled, you can disable + server flow control using the --disable-server-flow-control + If you are working with a large remote repository and a 'cvs + checkout' is swamping your network and memory, 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 by passing + ',', in bytes, as an argument to + --enable-server-flow-control. The low water mark defaults to one + megabyte and the high water mark defaults to two megabytes. + + $ ./configure --enable-server-flow-control=1M,2M + + The --with-tmpdir argument to configure may be used to set a + specific directory for use as a default temporary directory. If not + set, configure will pick the first directory it finds which it has + read, write, and execute permissions to from $TMPDIR, $TMP, $TEMP, + /tmp, and /var/tmp, in that order. Failing that, it will use /tmp. + + The --with-umask argument to configure can be used to change + the default umask used by the CVS server executable. + + Unlike previous versions of CVS, you do not need to install RCS + or GNU diff. + + 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. + + If you want to disable automatic dependency tracking in the makefiles, + use the '--disable-dependency-tracking' option: + + $ ./configure --disable-dependency-tracking + + This avoids problems on some platforms. See the note at the end of this + file on BSD. + + Try './configure --help' for further information on its usage. + + NOTE ON CVS's USE OF NDBM: + + By default, CVS uses some built-in ndbm emulation code to allow + CVS to work in a heterogeneous environment. However, if you have + a very large modules database, this may not work well. You will + need to supply the --disable-cvs-ndbm option to configure to + accomplish this. If you do this, the following comments apply. If + not, you may safely skip these comments. + + If you configure CVS to use the real ndbm(3) libraries and + you do not have them installed in a "normal" place, you will + probably want to get the GNU version of ndbm (gdbm) and install + that before running the CVS configure script. Be aware that the + GDBM 1.5 release does NOT install the header file included + with the release automatically. You may have to install it by hand. + + If you configure CVS to use the ndbm(3) libraries, you cannot + compile CVS with GNU cc (gcc) on Sun-4 SPARC systems. However, gcc + 2.0 may have fixed this limitation if -fpcc-struct-return is + defined. When using gcc on other systems to compile CVS, you *may* + need to specify the -fpcc-struct-return option to gcc (you will + *know* you have to if "cvs checkout" core dumps in some ndbm + function). You can do this as follows: + + $ CC='gcc -fpcc-struct-return' ./configure + + for sh, bash, and ksh users and: + + % setenv CC 'gcc -fpcc-struct-return' + % ./configure + + for csh and tcsh users. + + END OF NOTE FOR NDBM GUNK. + +2) Try to build it: + + $ make + + This will (hopefully) make the needed CVS binaries within the + "src" directory. If something fails for your system, and you want + to submit a bug report, you may wish to include your + "config.status" file, your host type, operating system and + compiler information, make output, and anything else you think + will be helpful. + +3) Run the regression tests (optional). + + You may also wish to validate the correctness of the new binary by + 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: + + $ make install + + Depending on your installation's configuration, you may need to be + root to do this. + +------------------------------------------------------------------------------- + +Detailed information about your interaction with "configure": + +The "configure" script and its interaction with its options and the +environment is described here. For more detailed documentation about +"configure", please run `./configure --help' or refer to the GNU Autoconf +documentation. + +Supported options are: + + --srcdir=DIR Useful for compiling on many different + machines sharing one source tree. + --prefix=DIR The root of where to install the + various pieces of CVS (/usr/local). + --exec_prefix=DIR If you want executables in a + host-dependent place and shared + things in a host-independent place. + +The following environment variables override configure's default +behaviour: + + CC If not set, tries to use gcc first, + then cc. Also tries to use "-g -O" + as options, backing down to -g + alone if that doesn't work. + INSTALL If not set, tries to use "install", then + "./install-sh" as a final choice. + RANLIB If not set, tries to determine if "ranlib" + is available, choosing "echo" if it doesn't + appear to be. + YACC If not set, tries to determine if "bison" + is available, choosing "yacc" if it doesn't + appear to be. + +------------------------------------------------------------------------------- + +Building from source code under Windows NT/95/98/2000: + +You may find interesting information in windows-NT/README. + +* Using Microsoft Visual C++ 5.x+. + +1) Using Microsoft Visual C++ 5.x+, open the project `cvsnt.dsw', + in the top directory of the CVS distribution. If you have an older + version of Visual C++, take a look at windows-NT/README. +2) Choose "Build cvs.exe" from the "Project" menu. +3) MSVC will place the executable file cvs.exe in WinRel, or whatever + your target directory is. + +* From the top level directory, with MSVC++ 5.x+ installed, something like the +following also works: + + C:\> vcvars32 + C:\> nmake /f cvsnt.mak CFG="cvsnt - Win32 Debug" + +* Using the Cygwin development environment , Windows clients + and servers can be built using the instructions for building on UNIX. For + deploying the CVS server on Windows NT, see the `cygrunsrv' executable that + comes with Cygwin. + +* You might also try & . + +------------------------------------------------------------------------------- + +Building from source code under other platforms: + +For OS/2, see os2/README and emx/README. + +For VMS, see README.VMS + +Mac OS X: Builds fine, just like UNIX. + +For older versions of Mac OS, you might try . + +For a Java client, see jCVS (which is a separate package from CVS +itself, but which might be preferable to the Macintosh port mentioned +above, for example). + +------------------------------------------------------------------------------- diff --git a/contrib/cvs-1.12.9/MINOR-BUGS b/contrib/cvs-1.12.9/MINOR-BUGS new file mode 100644 index 0000000000..9eb0e7b5f5 --- /dev/null +++ b/contrib/cvs-1.12.9/MINOR-BUGS @@ -0,0 +1,61 @@ +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: $ 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 ) + I tried a "cvs status -v" and received the following: + + ? CVS + ? programs/CVS + ? tests/CVS + cvs server: Examining . + =================================================================== + File: Install.dec Status: Up-to-date + ... + + I claim that CVS dirs should be ignored. + (This reportedly happens if "cvs add CVS" (or "cvs add *") + is followed by "cvs status", in client/server mode - CVS 1.9). + +* 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 + way to send this information along (according to cvsclient.texi). + Perhaps we can spiff it up a bit by using the conflict field for the + stamp on the checkout/update command. Please note that this really + doesn't do very much for us even if we get it done. + +* Does the function that lists the available modules in the repository + belong under the "checkout" function? Perhaps it is more logically + grouped with the "history" function or we should create a new "info" + function? diff --git a/contrib/cvs-1.12.9/NEWS b/contrib/cvs-1.12.9/NEWS new file mode 100644 index 0000000000..f75b952701 --- /dev/null +++ b/contrib/cvs-1.12.9/NEWS @@ -0,0 +1,1767 @@ +Changes since 1.12.8: +********************* + +SERVER SECURITY FIXES + +* Thanks to Stefan Esser & Sebastian Krahmer, several potential security + problems have been fixed. The ones which were considered dangerous enough + to catalogue were assigned issue numbers CAN-2004-0416, CAN-2004-0417, & + CAN-2004-0418 by the Common Vulnerabilities and Exposures Project. Please + see for more information. + +* A potential buffer overflow vulnerability in the server has been fixed. + This addresses the Common Vulnerabilities and Exposures Project's issue + #CAN-2004-0414. Please see for more information. + +NEW FEATURES + +* `cvs log' & `cvs ls' now output local times when both the server and client + are 1.12.9 or greater. + +DEVELOPER NOTES + +* The windows-NT/config.h.in file is now generated dynamically from the + root config.h.in file and a few inputs in the windows-NT directory in hopes + of keeping it more in sync with the root config.h.in file. + +Changes from 1.12.7 to 1.12.8: +****************************** + +SERVER SECURITY FIXES + +* A potential buffer overflow vulnerability in the server has been fixed. + Prior to this patch, a malicious client could potentially use carefully + crafted server requests to run arbitrary programs on the CVS server machine. + This addresses the Common Vulnerabilities and Exposures Project's issue + #CAN-2004-0396. Please see for more information. + +NEW FEATURES + +* Some redundant output generated by the `cvs commit' command has been removed. + +* Most output from the `cvs commit' command is suppressed when the -Q global + option is specified. + +* Repository directory browsing via `cvs rls' & `cvs ls' commands. Expect + changes in the long format output soon. The "entries" format output should + remain fairly stable for automated parsers. + +* Glob matches, as specified in ignore lists and wrapper options, now conform + to the POSIX.2 specification for fnmatch on all platforms. + +* The Windows MS Visual C++ project files, including the nmake build files, + are now generated with MSVC++ 6.0, but should still work with MSVC++ 5.0. + +BUG FIXES + +* The cvs.1 man page is now generated automatically from a section of the CVS + Manual. + +* Thanks to a report from Mark Andrews at the Internet Systems Consortium, the + :ext: connection method no longer relies on a transparent transport that uses + an argument processor that can handle arbitrary ordering of options and other + arguments when using a username other than the caller's. + +* Thanks to Ken Raeburn at MIT, directory deletion, whether via `cvs release' + or empty directory pruning, now works on network shares under Windows XP. + +Changes from 1.12.6 to 1.12.7: +****************************** + +SERVER SECURITY ISSUES + +* Piped checkouts of paths above $CVSROOT no longer work. Previously, clients + could have requested the contents of RCS archive files anywhere on a CVS + server. This addresses CVE issue CAN-2004-0405. Please see + for more information. + +CLIENT SECURITY ISSUES + +* Clients now check paths from the server to verify that they are within one of + the sandboxes the user requested be updated. Previously, a trojan server + could have written or overwritten files anywhere the user had access, + presenting a serious security risk. This addresses CVE issue CAN-2004-1080. + Please see for more information. + +GENERAL USER ISSUES + +* Imported the most recent version of regex from GNULIB, which actually means + some systems will use now their native regex functions instead of compiling + CVS's. Users should notice no changes in CVS responses to regular + expressions. If you do, please report them to . + +* CVS now accepts the location of HTTP tunnel web proxies as part of the + CVSROOT string. Actually using a proxy remains untested. Please report + problems and successes to . + +* Configure no longer checks the $TMPDIR, $TMP, & $TEMP variables to set the + default temporary directory. + +* CVS on Cygwin correctly handles X:\ style paths. + +* Import now uses backslash rather than slash on Windows when checking for + "CVS" directories to ignore in import commands. + +* Relative paths containing up-references (`..') should now work in + client/server mode (client fix). + +* A race condition between the ordering of messages from CVS and messages from + called scripts in client/server mode has been removed (server fix). + +* The check_cvs and cvscheck scripts in the contrib directory have been renamed + validate_repo and sandbox_status, respectively, in the interests of clarity. + +* The Windows MS Visual C++ 6.0 project files have been brought up to date. + The nmake build files were regenerated from these files with MSVC++ 5.0. + +* A memory allocation bug on Windows that could cause at least executions of + `cvs status' to fail has been fixed (client fix). + +* Resurrected files now get their modes and timestamps set correctly and a + longstanding bug involving resurrection of an uncommitted removal has been + fixed (server fix). + +* Some resurrection (cvs add) status messages have changed slightly. + +* `cvs release' now works with Kerberos or GSSAPI encryption enabled (server + fix). + +* File resurrection from a previously existing revision no longer just reports + that it works (server fix). + +* Misc error & status message corrections. + +* Diffing of locally added files against arbitrary revisions in an RCS archive + is now allowed when a file of the same name exists or used to exist on some + branch (server fix). + +* Some user messages have been updated for consistency and spelling. + +DEVELOPER ISSUES + +* The message source differentiation in the test suite between client and + server executables has been repaired. + +Changes from 1.12.5 to 1.12.6: +****************************** + +GENERAL USER ISSUES + +* CVSROOT/*info scripts may not work as expected with executables compiled + using VC++ under Windows since all quoting is currently done according to + Bourne Shell rules, which probably don't look like command.com rules. + Patches gratefully accepted. + +* Imports will now always ignore directories and files named `CVS' to avoid + violating assumptions made by other parts of CVS. + +* Directories specified to `checkout -d' are no longer required to exist. This + consolidates some behavior between `-d' options specified in the modules file + and `checkout -d' as well as removing some prior differences between local + and client/server mode operation. + +* A problem with `cvs release' of subdirs that could corrupt CVS/Entries files + has been fixed (client/server). + +* The CVS server's protocol check for unused data from the client is no longer + called automatically at program exit in order to avoid potential recursive + calls to error when the first close is due to memory allocation or similar + problems that cause calls to error() to fail. The check is still made when + the server program exits normally. + +* The CVSROOT/*info files want a new command format and the old style strings + have been deprecated. Please see the manual for more information on the new + format. + +* The spec file has been updated to work with more recent versions of RPM. + +* Some more GNULIB functions have been imported and/or updated for portability + reasons. + +* Several memory leaks have been plugged. + +* A seg fault which always occurred after waiting on another process's lock + in order to establish a promotable lock is now avoided. + +* An unlikely potential segfault when using the :fork: connection method has + been fixed. + +* The CVS server has had the protocol check for unused data from the client + partially restored. + +* A fix has been included that should avoid a very rare race condition that + could cause a CVS server to exit with a "broken pipe" message. + +* Infinite alias loops in the modules file are now checked for and avoided. + +* Clients on case insensitive systems now preserve the case of directories in + CVS/Entries, in addition to files, for use in communications with the CVS + server. + +* Misc status message fixes for consistency. + +* Some previously untested behavior is now being tested. + +* Server no longer claims to support the "Case" request. + +* Case insensitive clients once again preserve the case of filenames in + CVS/Entries for communication with the server, as specified in the CVS + client/server protocol spec. Note that all CVS _servers_ still lack support + for case insensitive clients - servers are relying on the client to preserve + the case of checked out files. + +* Thanks to Ville Skyttä the man page has a few less spelling errors and is + slightly more accurate. + +* Thanks to Ville Skyttä some unused variables were removed from the log_accum + Perl script in contrib. + +* Thanks to Alexey Mahotkin, a bug that prevented CVS from being compiled with + Kerberos 4 authentication enabled has been fixed. + +* A minor bug that caused CVS to fail to report an inifinte alias loop in the + modules file when portions of the alias definition contained trailing slashes + has been fixed. + +* A bug in the gzip code that could cause heap corruption and segfaults in CVS + servers talking to clients less than 1.8 and some modern third-party CVS + clients has been fixed. + +* mktemp.sh is now included with the source distribution so that the rcs2log + and cvsbug executables may be run on systems which do not contain an + implementation of mktemp. + +* Misc documentation fixes. + +DEVELOPER ISSUES + +* xmalloc, xstrdup, & some other memory allocating functions are now available + vi GNULIB versions imported into lib. + +* The asnprintf() & vasnprintf() functions are now available due to a GNULIB + implementation. + +* Misc cosmetic, readability, and commenting fixes. + +Changes between 1.12.4 and 1.12.5: +********************************** + +SERVER SECURITY ISSUES + +* pserver can no longer be configured to run as root via the + $CVSROOT/CVSROOT/passwd file, so if your passwd file is compromised, it no + longer leads directly to a root hack. Attempts to root will also be logged + via the syslog. + +GENERAL USER ISSUES + +* The Windows build files were updated to allow building of the current version + under Windows. + +Changes between 1.12.3 and 1.12.4: +********************************** + +GENERAL USER ISSUES + +* The CVS server no longer locks more than a directory at a time for write, so + large commits & tags should now have a much harder time blocking other + operations. + +* Add support for large files. Use --disable-largefile to omit support + for large files. + +Changes between 1.12.2 and 1.12.3: +********************************** + +SERVER SECURITY ISSUES + +* Malformed module requests could cause the CVS server to attempt to create + directories and possibly files at the root of the filesystem holding the CVS + repository. Filesystem permissions usually prevent the creation of these + misplaced directories, but nevertheless, the CVS server now rejects the + malformed requests. + +GENERAL USER ISSUES + +* Support for case insensitive clients has been removed. This is not as + drastic as it sounds, as all of the current tests still pass without + modification when run from a case insensitive client to a case sensitive + server. In the end this should provide a major stability improvement. + +* A minor problem that prevented the correct version of a system ZLIB from + being detected on some platforms has been fixed. + +* Attempts to use the global `-l' option, removed from both client and server + as of version 1.12.1, will now elicit a warning rather than a fatal error + from the server. + +* The configure script now tests whether it is building CVS on a case + insensitive file system. If it is, CVS assumes that all file systems on this + platform will be case insensitive. This is useful for getting the case + insensitivity flag set correctly when compiling on Mac OS X and under Cygwin + on Windows. Autodetection can be overridden using the + --disable-case-sensitivity and --enable-case-sensitivity arguments to + configure. + +DEVELOPER ISSUES + +* A new set of tests to test issues specific to case insensitive clients and + servers has also been added. + +* Support has been added to the test suite to support testing over a :ext: link + to another machine, subject to some stringent requirements. This support can + be used, for instance, to test the operation of a case insensitive client + against a case sensitive server. Please see the comments in TEST and the + src/sanity.sh test script itself for more. + +* We've standardized on Automake 1.7.9 to get a bug fix. See the note below + on the Autoconf upgrade for more details. + +* We've standardized on Autoconf version 2.58 to avoid a bug and get at a few + new macros. Again, this should only really affect developers, though it is + possible that CVS will now compile on a few new platforms. Please see the + section of the INSTALL file about using the autotools if you are compiling + CVS yourself. + +Changes between 1.12.1 and 1.12.2: + +* Misc cleanup, reorganization, and other minor fixes. + +* A behavior change in `cvs up -jrev1 -jrev2' for modified files with a base + revision of rev2 (ie, checked-out version matches rev2 and file has been + modified). The operation is no longer ignored and instead is passed to + diff3. This will potentially re-apply the diffs between the two revisions to + a modified local file. Status messages like from a standard merge have also + been added when the file would not or does not change due to this merge + request ("[file] already contains the changes between [revisions]..."). + +* A build problem that caused warnings and slower builds on systems without a +working getline() function (e.g. Mac OS X 10.1) has been fixed. + +* A build problem that prevented the CVS executable from being built on systems +with the gettext library installed has been fixed. + +* A bug which could stop `cvs admin -mTAG:message' from recursing has been + fixed. + +* Misc documentation cleanup and fixes. + +* Some of the contrib scripts, some of the documentation, and sanity.sh were + modified to use and recommend more portable commands rather than using and + recommending commands which were not compatible with the POSIX 1003.1-2001 + specification. + +* CVS now knows how to report, as well as record, `P' record types. + +* When running the `cvs history' command, clients will now send the + long-accepted `-e' option, for all records, rather than explicitly requesting + `P' record types, a request which servers prior to 1.11.7 will reject with a + fatal error message. + +* A problem with locating files requested by case insensitive clients which was + accidentally introduced in 1.11.6 as part of a fix for a data loss problem + involving `cvs add's from case insensitive clients has been fixed. The + relevant error message was `cvs [ aborted]: filE,v is ambiguous; + could mean FILE,v or file,v'. + +* A problem in the CVS getpass library that could cause passwords to echo on + some systems has been fixed. + +* A segfault that could occur in very rare cases where the stat of a file + failed during a diff has been fixed. + +* Any user with write privleges to the CVSROOT/checkoutlist file could pass +arbitrary format strings directly through to a printf function. This was +probably bad and has been fixed. White space at the beginning of error strings +in checkoutlist is now ignored properly. + +* A chmod 0600 that CVS performed on temp files it created designed to work +around a bug in versions of GLIBC eariler than 2.0.7 has been removed since it +still left a race condition open to exploitation and provided a false sense of +security. If you are linking CVS against a version of GLIBC prior to 2.0.7, +you should consider upgrading GLIBC. + +* The CVSROOT/editinfo file is no longer referenced by CVS. This funcitonality +has been deprecated for over six years and removing it will presumably not +cause anyone any problems. + +* In client/server mode, most messages from CVS now contain the actual +command name rather than the generic "server". + +* A long-standing bug that prevented most client/server updates from being +logged in the history file has been fixed. + +* Updates done via a patch ("P" status) are now logged in the history file +by default and the corresponding "P" history record type is now documented. +If you're setting the LogHistory option in your CVSROOT/config file, you may +want to add "P" to the list of record types. + +* CVS now will always compile its own getpass() function (originally from +GNULIB) in favor of any system one that may exist. This avoids some problems +with long passwords on some systems and updates us to POSIX.2 compliance, since +getpass() was removed from the POSIX.2 specification. + +* Support for pre-ANSI compilers has been removed. Our minimum support level +now assumes at least a freestanding C89 compilers. See the HACKING file for +more information. If you *really* need K&R support, our Makefile.am files +should only need minor tweaking to get them to run the ansi2knr script from the +Automake project. If you get this working, please send a patch to +. + +* Experimental support for Pluggable Authentication Modules (PAM) has been +added, though it is not compiled by default. If you like this feature (or +don't), please send us feedback. See the Cederqvist, `./configure --help', +and the INSTALL file for more. + +* Command line keyword expansion modes no longer override binary keyword +expansion modes. + +* New LocalKeyword and KeywordExpand options to CVSROOT/config which +FreeBSD, OpenBSD, and NetBSD users may find familiar as the "tag" and +"tagexpand" options used for many years. The CVSHeader keyword has +also been added to the mixture. + +* A bug that allowed a write lock to be created in a directory despite +there being existing read locks when using LockDir in CVSROOT/config has +been fixed. + +* A bug with short patches (`rdiff -s') which caused rdiff to sometimes report +differences that did not exist has been fixed. + +* Some minor corrections were made to the diff code to keep diff & rdiff from +printing diff headers with empty change texts when two files have different +revision numbers but the same content. + +* The global '-l' option, which suppressed history logging, has been removed +from both client and server. + +Changes from 1.11.5 to 1.12.1: + +* The new --with-external-zlib option can be passed to configure to compile +CVS against an external installed zlib. + +* A warning message is now issued if an administrative file contains +more than one DEFAULT entry. + +* An error running a verifymsg script (such as referencing an unset user +variable or the script not existing) now causes the verification to +fail. + +* Errors in administrative files commands (like unset user variables) +are no longer reported unless the command is actually executed. + +* When a file is initially checked out, its last access time is now set +to the current time rather than being set to the time the file was last +checked in like the modification time is. + +* The Checkin.prog and Update.prog functionality has been removed. This +fuctionality previously allowed executables to be specified in the modules file +to be run at update and checkin time, but users could edit these files on a per +workspace basis, creating a security hole. + +* CVSROOTs which contain a symlink to a real repository should work. + +* contrib/rcs2log and src/cvsbug now use the BSD mktemp program to create +their temp files and directories on systems which provide it. + +* Added a UserAdminOptions configuration option to CVSROOT/config to +control which `cvs admin' commands are not restricted to the `cvsadmin' +group. + +* If the rcsinfo specified template changes after a user has checked +out a tree, the template in the users' tree will be updated rather +than remaining static from the time of the original checkout. + +* Added a CVSREADONLYFS environment variable and `-R' cvs global +option to turn on read-only repository mode for local repositories. +This allows users to checkout from a CDROM repository or other +read-only filesystem. + +* There is a new CVS_LOCAL_BRANCH_NUM environment variable, which +may be used to give control over the branch number to be used next. +Useful for having local changes in a CVSup mirrored repository. + +* Miscellaneous documentation corrections. + +* Corrected the path in a failed write error message. + +* Autoconf and Automake are no longer run automatically unless you run +configure with --enable-maintainer-mode. Accordingly, noautomake.sh is +no longer needed and has been removed. + +* We've standardized on Automake version 1.7.5 and Autoconf version 2.57 to get +at a few new macros. Again, this should only really affect developers. See +the section of the INSTALL file about using the autotools if you are compiling +CVS yourself. + +Changes from 1.11.4 to 1.11.5: + +* Fixed a security hole in the CVS server by which users with read only access +could gain write access. This issue does not affect client builds. The +Common Vulnerabilities and Exposures project (cve.mitre.org) has assigned the +name CAN-2003-0015 to this issue. See + for more +information. + +* Fixed some bugs where revision numbers starting with 0 (like 0.3) +weren't correctly handled. (CVS doesn't normally use such revision +numbers, but users may be able to force it to do so and old RCS files +might.) + +Changes from 1.11.3 to 1.11.4: + +* Some minor changes to allow the code to compile on Windows platforms. + +Changes from 1.11.2 to 1.11.3: + +* The tag/rtag code has been fixed to once again lock just a single +directory at a time. + +* There was a bug where certain error conditions could cause the server +to go into an infinite loop. There was also a bug that caused a +compressed connection from an older client to hang on shutdown. These +bugs have been fixed. + +* Fixed a bug that caused the server to reject most watch commands. + +* When waiting for another user's lock, the message timestamps are now +in UTC rather than the server's local time. + +* The options.h file is no longer used. This fixes a bug that occurred when +1.11.2 was compiled on Windows platforms. + +* We've standardized on Automake version 1.6.3 and Autoconf version 2.53. +They are cleaner, less bug prone, and will hopfully allow me to start updating +sanity.sh to use Autotest and Autoshell. Again, this should only really affect +developers. See the section of the INSTALL file about using the autotools if +you are compiling CVS yourself. + +* Fixed a bug in the log/rlog code when a revision range crosses a +branch point. + +* Fixed a bug where filenames starting with - would be misinterpreted as +options when using client/server mode. + +Changes from 1.11.1p1 to 1.11.2: + +* There is a new feature, enabled by RereadLogAfterVerify in CVSROOT/config, +which tells CVS to reread the log message after running the verifymsg +script. This allows the verifymsg script to reformat or otherwise +modify the log message. + +* The interpretation of revision ranges using :: in "log" and "rlog" +has changed: a::b now excludes the log message from revision a but +includes the log message from revision b. Also, revision ranges that +cross branch points should now work. + +* zlib has been updated to version 1.4. There is a security advisory +out in regards to 1.3. This should fix that problem. + +* The "log" and "rlog" commands now have a -S option to suppress the +header information when no revisions are selected. + +* A serious error that allowed read-only users to tag files has been +corrected. + +* The "annotate" command will no longer annotate binary files unless +you specify the new -F option. + +* The "tag" and "rtag" commands will no longer move or delete branch +tags unless you use the new -B option. (This prevents accidental +changes to branch tags that are hard to undo.) + +* We've standardized on the 1.5 Automake release for the moment. Again, this +should only really affect developers. See the section of the INSTALL file +about using the autotools if you are compiling CVS yourself. + +Changes from 1.11.1 to 1.11.1p1: + +* Read only access was broken - now fixed. + +Changes from 1.11 to 1.11.1: + +* There was a locking bug in the tag/rtag code that could lose changes +made to a file while the tag operation was in progress. This has been +fixed, but all of the directories being tagged are now locked for the +entire duration of the tag operation rather than only one directory at a +time. + +* The "cvs diff" command now accepts the -y/--side=by-side and -T/ +--initial-tab options. (To use these options with a remote repository, +both the client and the server must support them.) + +* The expansion of the loginfo format string has changed slightly. +Previously, the expansion was surrounded by single quotes ('); if a file +name contained a single quote character, the string would not be parsed +as a single entity by the Unix shell (and it would not be possible to +parse it unambiguously). Now the expansion is surrounded by double +quotes (") and any embedded dollar signs ($), backticks (`), backslashes +(\), and double quotes are preceded by a backslash. This is parsed as a +single entity by the shell reguardless of content. This change should +not be noticable unless you're not using a Unix shell or you have +embedded the format string inside a double quoted string. + +* There was a bug in the diff code which sometimes caused conflicts to +be flagged which shouldn't have been. This has been fixed. + +* New "cvs rlog" and "cvs rannotate" commands have been added to get log +messages and annotations without having to have a checked-out copy. + +* Exclusive revision ranges have been added to "cvs log" using :: +(similar to "cvs admin -o"). + +* The VMS client now accepts wildcards if you're running VMS 7.x. + +* ZLIB has been updated to version 1.1.3, the most current version. This +includes mostly some optimizations and minor bug fixes. + +* The ~/.cvspass file has a slightly modified format. CVSROOTs are now +stored in a new canonical form - hostnames are now case insensitive and +port numbers are always stored in the new format. Until a new login for +a particular CVSROOT is performed with the new version of CVS, new and +old versions of CVS should interoperate invisibly. After that point, an +extra login using the old version of CVS may be necessary to continue to +allow the new and old versions of CVS to interoperate using the same +~/.cvspass file and CVSROOT. The exception to this rule occurs when the +CVSROOTs used with the different versions use case insensitively +different hostnames, for example, "empress", and "empress.2-wit.com". + +* A password and a port number may now be specified in CVSROOT for +pserver connections. The new format is: + + :pserver:[[user][:password]@]host[:[port]]/path + +Note that passwords specified in a checkout command will be saved in the +clear in the CVS/Root file in each created directory, so this is not +recommended, except perhaps when accessing anonymous repositories or the +like. + +* The distribution has been converted to use Automake. This shouldn't +affect most users except to ease some portability concerns, but if you +are building from the repository and encounter problems with the +makefiles, you might try running ./noautomake.sh after a fresh update +-AC. + +Changes from 1.10 to 1.11: + +* The "cvs update" command has a new -C option to get clean copies from +the repository, abandoning any local changes. + +* The new "cvs version" command gives a short version message. If +the repository is remote, both the client and server versions are +reported. + +* "cvs admin -t" now works correctly in client/server mode. + +* The "cvs history" command output format has changed -- the date +now includes the year and is given is ISO 8601 format (yyyy-mm-dd). +Also, the new LogHistory option in CVSROOT/config can be used to +control what information gets recorded in the log file and code has +been added to record file removals. + +* The buggy PreservePermissions code has been disabled. + +* Anonymous read-only access can now be done without requiring a +password. On the server side, simply give that user (presumably +`anonymous') an empty password in the CVSROOT/passwd file, and then +any received password will authenticate successfully. + +* There is a new access method :fork: which is similar to :local: +except that it is implemented via the CVS remote protocol, and thus +has a somewhat different set of quirks and bugs. + +* The -d command line option no longer updates the CVS/Root file. For +one thing, the CVS 1.9/1.10 behavior never had updated CVS/Root in +subdirectories, and for another, it didn't seem that popular in +general. So this change restores the CVS 1.8 behavior (which is also +the CVS 1.9/1.10 behavior if the environment variable +CVS_IGNORE_REMOTE_ROOT is set; with this change, +CVS_IGNORE_REMOTE_ROOT no longer has any effect). + +* It is now possible for a single CVS command to recurse into several +CVS roots. This includes roots which are located on several servers, +or which are both remote and local. CVS will make connections to as +many servers as necessary. + +* It is now possible to put the CVS lock files in a directory +set by the new LockDir option in CVSROOT/config. The default +continues to be to put the lock files in the repository itself. + +Changes from 1.9 to 1.10: + +* A bug was discovered in the -t/-f wrapper support that can cause +serious data loss. Because of this (and also the fact that it doesn't +work at all in client/server mode), the -t/-f wrapper code has been +disabled until it can be fixed. + +* There is a new feature, enabled by TopLevelAdmin in CVSROOT/config, +which tells CVS to modify the behavior of the "checkout" command. The +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. See the Cederqvist for details. + +* There is an optional set of features, enabled by PreservePermissions +in CVSROOT/config, which allow CVS to store unix-specific file +information such as permissions, file ownership, and links. See the +Cederqvist for details. + +* One can now authenticate and encrypt using the GSSAPI network +security interface. For details see the Cederqvist's description of +specifying :gserver: in CVSROOT, and the -a global option. + +* All access to RCS files is now implemented internally rather than by +calling RCS programs. The main user-visible consequence of this is +that there is no need to worry about making sure that CVS finds the +correct version of RCS. The -b global option and the RCSBIN setting +in CVSROOT/config are still accepted but don't do anything. The +$RCSBIN internal variable in administrative files is no longer +accepted. + +* There is a new syntax, "cvs admin -orev1::rev2", which collapses the +revisions between rev1 and rev2 without deleting rev1 or rev2 +themselves. + +* There is a new administrative file CVSROOT/config which allows one +to specify miscellaneous aspects of CVS configuration. Currently +supported here: + + - SystemAuth, allows you to prevent pserver from checking for system + usernames/passwords. + +For more information see the "config" section of cvs.texinfo. + +* When setting up the pserver server, one now must specify the +allowable CVSROOT directories in inetd.conf. See the Password +authentication server section of cvs.texinfo for details. Note that +this implies that everyone who is running a pserver server must edit +inetd.conf when upgrading their CVS. + +* 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, including a discussion of security issues. +Note that the requirement that read-only users be able to create locks +and write the history file still applies. + +* 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 +modification, and date of the modification. For more information see +the `annotate' node in cvs.texinfo. + +* The cvsinit shell script has been replaced by a cvs init command. +The cvs init command creates some example administrative files which +are similar to the files found in the examples directory (and copied +by cvsinit) in previous releases. + +* Added the patterns *.olb *.exe _$* *$ to default ignore list. + +* There is now a $USER internal variable for *info files. + +* There is no longer a separate `mkmodules' program; the functionality +is now built into `cvs'. If upgrading an old repository, it is OK to +leave in the lines in the modules file which run mkmodules (the +mkmodules actions will get done twice, but that is harmless); you will +probably want to remove them once you are no longer using the old CVS. + +* One can now specify user variables in *info files via the +${=varname} syntax; there is a -s global option to set them. See the +Variables node in cvs.texinfo for details. + +Changes from 1.6 to 1.7: + +* The default ignore list has changed slightly: *.obj has been added +and CVS* has been changed to CVS CVS.adm. + +* CVS now supports password authentication when accessing remote +repositories; this is useful for sites that can't use rsh (because of +a firewall, for example), and also don't have kerberos. See node +"Password authenticated" (in "Remote repositories", in +doc/cvs.texinfo) for more details. Note: This feature requires both +the client and server to be upgraded. + +* Using the -kb option to specify binary files now works--most cases +did not work before. See the "Binary files" section of +doc/cvs.texinfo for details. + +* New developer communication features. See the "Watches" section of +doc/cvs.texinfo for details. + +* RCS keyword "Name" supported for "cvs update -r " and "cvs +checkout -r ". + +* If there is a group whose name matches a compiled in value which +defaults to "cvsadmin", only members of that group can use "cvs +admin". This replaces the CVS_NOADMIN option. + +* CVS now sets the modes of files in the repository based on the +CVSUMASK environment variable or a compiled in value defaulting to +002. This way other developers will be able to access the files in +the repository regardless of the umask of the developer creating them. + +* The command names in .cvsrc now match the official name of the +command, not the one (possibly an alias) by which it was invoked. If +you had previously relied on "cvs di" and "cvs diff" using different +options, instead use a shell function or alias (for example "alias +cvsdi='cvs diff -u'"). You also can specify global CVS options (like +"-z") using the command name "cvs". + +Changes from 1.5 to 1.6: + +* Del updated the man page to include all of the new features +of CVS 1.6. + +* "cvs tag" now supports a "-r | -D" option for tagging an already +tagged revision / specific revision of a file. + +* There is a "taginfo" file in CVSROOT that supports filtering and +recording of tag operations. + +* Long options support added, including --help and --version options. + +* "cvs release" no longer cares whether or not the directory being +released has an entry in the `modules' file. + +* The modules file now takes a -e option which is used instead of -o +for "cvs export". If your modules file has a -o option which you want +to be used for "cvs export", change it to specify -e as well as -o. + +* "cvs export" now takes a -k option to set RCS keyword expansion. +This way you can export binary files. If you want the old behavior, +you need to specify -kv. + +* "cvs update", "cvs rdiff", "cvs checkout", "cvs import", "cvs +release", "cvs rtag", and "cvs tag" used to take -q and -Q options +after the command name (e.g. "cvs update -q"). This was confusing +because other commands, such as "cvs ci", did not. So the options +after the command name have been removed and you must now specify, for +example, "cvs -q update", which has been supported since CVS 1.3. + +* New "wrappers" feature. This allows you to set a hook which +transforms files on their way in and out of cvs (apparently on the +NeXT there is some particular usefulness in tarring things up in the +repository). It also allows you to declare files as merge-by-copy +which means that instead of trying to merge the file, CVS will merely +copy the new version. There is a CVSROOT/cvswrappers file and an +optionsl ~/.cvswrappers file to support this feature. + +* You can set CVSROOT to user@host:dir, not just host:dir, if your +username on the server host is different than on the client host. + +* VISUAL is accepted as well as EDITOR. + +* $CVSROOT is expanded in *info files. + +Changes from 1.4A2 to 1.5: + +* Remote implementation. This is very helpful when collaborating on a +project with someone across a wide-area network. This release can +also be used locally, like other CVS versions, if you have no need for +remote access. + +Here are some of the features of the remote implementation: +- It uses reliable transport protocols (TCP/IP) for remote repository + access, not NFS. NFS is unusable over long distances (and sometimes + over short distances) +- It transfers only those files that have changed in the repository or + the working directory. To save transmission time, it will transfer + patches when appropriate, and can compress data for transmission. +- The server never holds CVS locks while waiting for a reply from the client; + this makes the system robust when used over flaky networks. + +The remote features are documented in doc/cvsclient.texi in the CVS +distribution, but the main doc file, cvs.texinfo, has not yet been +updated to include the remote features. + +* Death support. See src/README-rm-add for more information on this. + +* Many speedups, especially from jtc@cygnus.com. + +* CVS 1.2 compatibility code has been removed as a speedup. If you +have working directories checked out by CVS 1.2, CVS 1.3 or 1.4A2 will +try to convert them, but CVS 1.5 and later will not (if the working +directory is up to date and contains no extraneous files, you can just +remove it, and then check out a new working directory). Likewise if +your repository contains a CVSROOT.adm directory instead of a CVSROOT +directory, you need to rename it. + +Fri Oct 21 20:58:54 1994 Brian Berliner + + * Changes between CVS 1.3 and CVS 1.4 Alpha-2 + + * A new program, "cvsbug", is provided to let you send bug reports + directly to the CVS maintainers. Please use it instead of sending + mail to the info-cvs mailing list. If your build fails, you may + have to invoke "cvsbug" directly from the "src" directory as + "src/cvsbug.sh". + + * A new User's Guide and Tutorial, written by Per Cederqvist + of Signum Support. See the "doc" directory. A + PostScript version is included as "doc/cvs.ps". + + * The Frequesntly Asked Questions file, FAQ, has been added to the + release. Unfortunately, its contents are likely out-of-date. + + * The "cvsinit" shell script is now installed in the $prefix/bin + directory like the other programs. You can now create new + CVS repositories with great ease. + + * Index: lines are now printed on output from 'diff' and 'rdiff', + in order to facilitate application of patches to multiple subdirs. + + * Support for a ~/.cvsrc file, which allows you to specify options + that are always supposed to be given to a specific command. This + feature shows the non-orthogonality of the option set, since while + there may be an option to turn something on, the option to turn + that same thing off may not exist. + + * You can now list subdirectories that you wish to ignore in a + modules listing, such as: + + gcc -a gnu/gcc, !gnu/gcc/testsuites + + which will check out everything underneath gnu/gcc, except + everything underneath gnu/gcc/testsuites. + + * It is now much harder to accidentally overwrite an existing tag + name, since attempting to move a tag name will result in a error, + unless the -F (force) flag is given to the tag subcommands. + + * Better error checking on matching of the repository used to + check code out from against the repository the current cvs + commnands would use. (Thanks to Mark Baushke ) + + * Better support for sites with multiple CVSROOT repositories has + been contributed. The file "CVS/Root" in your working directory + is created to hold the full path to the CVS repository and a + simple check is made against your current CVSROOT setting. + + * You can now specify an RCS keyword substitution value when you + import files into the repository. + + * Uses a much newer version of Autoconf, and conforms to the GNU + coding standards much more closely. No, it still doesn't have + long option names. + + * Code cleanup. Many passes through gcc -Wall helped to identify + a number of questionable constructs. Most arbitrary length limits + were removed. + + * Profiling to determine bottlenecks helped to identify the best + places to spend time speeding up the code, which was then done. A + number of performance enhancements in filename matching have sped + up checkouts. + + * Many more contributions have been added to the "contrib" + directory. See the README file in that directory for more + information. + + * "cvs commit" will try harder to not change the file's + modification time after the commit. If the file does not change + as a result of the commit operation, CVS will preserve the + original modification time, thus speeding up future make-type + builds. + + * "cvs commit" now includes any removed files in the (optional) + pre-commit checking program that may be invoked. Previously, only + added and modified files were included. + + * It is now possible to commit a file directly onto the trunk at a + specific revision level by doing "cvs commit -r3.0 file.c", where + "3.0" specifies the revision you wish to create. The file must be + up-to-date with the current head of the trunk for this to succeed. + + * "cvs commit" will now function with a pre-commit program that + has arguments specified in the "commitinfo" file. + + * The "mkmodules" program will now look within the + $CVSROOT/CVSROOT/checkoutlist" file for any additional files that + should be automatically checked out within CVSROOT; mkmodules also + tries harder to preserve any execute bits the files may have + originally had. + + * "cvs diff" is much more accurate about its exit status now. It + now returns the maximum exit status of any invoked diff. + + * The "-I !" option is now supported for the import and update + commands correctly. It will properly clear the ignore list now. + + * Some problems with "cvs import" handling of .cvsignore have been + fixed; as well, some rampant recursion problems with import have + also been fixed. + + * "cvs rdiff" (aka "cvs patch") now tries to set the modify time + of any temporary files it uses to match those specified for the + particular revision. This allows a more accurate patch image to + be created. + + * "cvs status" has improved revision descriptions. "Working + revision" is used for the revision of the working file that you + edit directly; "Repository revision" is the revision of the file + with the $CVSROOT source repository. Also, the output is clearer + with regard to sticky and branch revisions. + + * CVS no longer dumps core when given a mixture of directories and + files in sub-directories (as in "cvs ci file1 dir1/file2"). + Instead, arguments are now clumped into their respective directory + and operated on in chunks, together. + + * If the CVSEDITOR environment variable is set, that editor is + used for log messages instead of the EDITOR environment variable. + This makes it easy to substitute intelligent programs to make more + elaborate log messages. Contributed by Mark D Baushke + (mdb@cisco.com). + + * Command argument changes: + cvs: The "-f" option has been added to ignore + the ~/.cvsrc file. + commit: Renamed the "-f logfile" option to the + "-F logfile" option. Added the "-f" + option to force a commit of the specified + files (this disables recursion). + history: Added "-t timezone" option to force any + date-specific output into the specified + timezone. + import: Added "-d" option to use the file's + modification time as the time of the + import. Added "-k sub" option to set the + default RCS keyword substitution mode for + newly-created files. + remove: Added "-f" option to force the file's + automatic removal if it still exists in + the working directory (use with caution). + rtag: Added "-F" option to move the tag if it + already exists -- new default is to NOT + move tags automatically. + tag: Added "-F" option to move the tag if it + already exists -- new default is to NOT + move tags automatically. + +Tue Apr 7 15:55:25 1992 Brian Berliner (berliner at sun.com) + + * Changes between CVS 1.3 Beta-3 and official CVS 1.3! + + * A new shell script is provided, "./cvsinit", which can be run at + install time to help setup your $CVSROOT area. This can greatly + ease your entry into CVS usage. + + * The INSTALL file has been updated to include the machines on + which CVS has compiled successfully. I think CVS 1.3 is finally + portable. Thanks to all the Beta testers! + + * Support for the "editinfo" file was contributed. This file + (located in $CVSROOT/CVSROOT) can be used to specify a special + "editor" to run on a per-directory basis within the repository, + instead of the usual user's editor. As such, it can verify that + the log message entered by the user is of the appropriate form + (contains a bugid and test validation, for example). + + * The manual pages cvs(1) and cvs(5) have been updated. + + * The "mkmodules" command now informs you when your modules file + has duplicate entries. + + * The "add" command now preserves any per-directory sticky tag when + you add a new directory to your checked-out sources. + + * The "admin" command is now a fully recursive interface to the + "rcs" program which operates on your checked-out sources. It no + longer requires you to specify the full path to the RCS file. + + * The per-file sticky tags can now be effectively removed with + "cvs update -A file", even if you had checked out the whole + directory with a per-directory sticky tag. This allows a great + deal of flexibility in managing the revisions that your checked-out + sources are based upon (both per-directory and per-file sticky + tags). + + * The "cvs -n commit" command now works, to show which files are + out-of-date and will cause the real commit to fail, or which files + will fail any pre-commit checks. Also, the "cvs -n import ..." + command will now show you what it would've done without actually + doing it. + + * Doing "cvs commit modules" to checkin the modules file will no + properly run the "mkmodules" program (assuming you have setup your + $CVSROOT/CVSROOT/modules file to do so). + + * The -t option in the modules file (which specifies a program to + run when you do a "cvs rtag" operation on a module) now gets the + symbolic tag as the second argument when invoked. + + * When the source repository is locked by another user, that user's + login name will be displayed as the holder of the lock. + + * Doing "cvs checkout module/file.c" now works even if + module/file.c is in the Attic (has been removed from main-line + development). + + * Doing "cvs commit */Makefile" now works as one would expect. + Rather than trying to commit everything recursively, it will now + commit just the files specified. + + * The "cvs remove" command is now fully recursive. To schedule a + file for removal, all you have to do is "rm file" and "cvs rm". + With no arguments, "cvs rm" will schedule all files that have been + physically removed for removal from the source repository at the + next "cvs commit". + + * The "cvs tag" command now prints "T file" for each file that was + tagged by this invocation and "D file" for each file that had the + tag removed (as with "cvs tag -d"). + + * The -a option has been added to "cvs rtag" to force it to clean + up any old, matching tags for files that have been removed (in the + Attic) that may not have been touched by this tag operation. This + can help keep a consistent view with your tag, even if you re-use + it frequently. + +Sat Feb 29 16:02:05 1992 Brian Berliner (berliner at sun.com) + + * Changes between CVS 1.3 Beta-2 and CVS 1.3 Beta-3 + + * Many portability fixes, thanks to all the Beta testers! With any + luck, this Beta release will compile correctly on most anything. + Hey, what are we without our dreams. + + * CVS finally has support for doing isolated development on a + branch off the current (or previous!) revisions. This is also + extremely nice for generating patches for previously released + software while development is progressing on the next release. + Here's an example of creating a branch to fix a patch with the 2.0 + version of the "foo" module, even though we are already well into + the 3.0 release. Do: + + % cvs rtag -b -rFOO_2_0 FOO_2_0_Patch foo + % cvs checkout -rFOO_2_0_Patch foo + % cd foo + [[ hack away ]] + % cvs commit + + A physical branch will be created in the RCS file only when you + actually commit the change. As such, forking development at some + random point in time is extremely light-weight -- requiring just a + symbolic tag in each file until a commit is done. To fork + development at the currently checked out sources, do: + + % cvs tag -b Personal_Hack + % cvs update -rPersonal_Hack + [[ hack away ]] + % cvs commit + + Now, if you decide you want the changes made in the Personal_Hack + branch to be merged in with other changes made in the main-line + development, you could do: + + % cvs commit # to make Personal_Hack complete + % cvs update -A # to update sources to main-line + % cvs update -jPersonal_Hack # to merge Personal_Hack + + to update your checked-out sources, or: + + % cvs checkout -jPersonal_Hack module + + to checkout a fresh copy. + + To support this notion of forked development, CVS reserves + all even-numbered branches for its own use. In addition, CVS + reserves the ".0" and ".1" branches. So, if you intend to do your + own branches by hand with RCS, you should use odd-numbered branches + starting with ".3", as in "1.1.3", "1.1.5", 1.2.9", .... + + * The "cvs commit" command now supports a fully functional -r + option, allowing you to commit your changes to a specific numeric + revision or symbolic tag with full consistency checks. Numeric + tags are useful for bringing your sources all up to some revision + level: + + % cvs commit -r2.0 + + For symbolic tags, you can only commit to a tag that references a + branch in the RCS file. One created by "cvs rtag -b" or from + "cvs tag -b" is appropriate (see below). + + * Roland Pesch and K. Richard Pixley + were kind enough to contribute two new manual + pages for CVS: cvs(1) and cvs(5). Most of the new CVS 1.3 features + are now documented, with the exception of the new branch support + added to commit/rtag/tag/checkout/update. + + * The -j options of checkout/update have been added. The "cvs join" + command has been removed. + + With one -j option, CVS will merge the changes made between the + resulting revision and the revision that it is based on (e.g., if + the tag refers to a branch, CVS will merge all changes made in + that branch into your working file). + + With two -j options, CVS will merge in the changes between the two + respective revisions. This can be used to "remove" a certain delta + from your working file. E.g., If the file foo.c is based on + revision 1.6 and I want to remove the changes made between 1.3 and + 1.5, I might do: + + % cvs update -j1.5 -j1.3 foo.c # note the order... + + In addition, each -j option can contain on optional date + specification which, when used with branches, can limit the chosen + revision to one within a specific date. An optional date is + specified by adding a colon (:) to the tag, as in: + + -jSymbolic_Tag:Date_Specifier + + An example might be what "cvs import" tells you to do when you have + just imported sources that have conflicts with local changes: + + % cvs checkout -jTAG:yesterday -jTAG module + + which tells CVS to merge in the changes made to the branch + specified by TAG in the last 24 hours. If this is not what is + intended, substitute "yesterday" for whatever format of date that + is appropriate, like: + + % cvs checkout -jTAG:'1 week ago' -jTAG module + + * "cvs diff" now supports the special tags "BASE" and "HEAD". So, + the command: + + % cvs diff -u -rBASE -rHEAD + + will effectively show the changes made by others (in unidiff + format) that will be merged into your working sources with your + next "cvs update" command. "-rBASE" resolves to the revision that + your working file is based on. "-rHEAD" resolves to the current + head of the branch or trunk that you are working on. + + * The -P option of "cvs checkout" now means to Prune empty + directories, as with "update". The default is to not remove empty + directories. However, if you do "checkout" with any -r options, -P + will be implied. I.e., checking out with a tag will cause empty + directories to be pruned automatically. + + * The new file INSTALL describes how to install CVS, including + detailed descriptions of interfaces to "configure". + + * The example loginfo file in examples/loginfo has been updated to + use the perl script included in contrib/log.pl. The nice thing + about this log program is that it records the revision numbers of + your change in the log message. + + Example files for commitinfo and rcsinfo are now included in the + examples directory. + + * All "#if defined(__STDC__) && __STDC__ == 1" lines have been + changed to be "#if __STDC__" to fix some problems with the former. + + * The lib/regex.[ch] files have been updated to the 1.3 release of + the GNU regex package. + + * The ndbm emulation routines included with CVS 1.3 Beta-2 in the + src/ndbm.[ch] files has been moved into the src/myndbm.[ch] files + to avoid any conflict with the system header file. If + you had a previous CVS 1.3 Beta release, you will want to "cvs + remove ndbm.[ch]" form your copy of CVS as well. + + * "cvs add" and "cvs remove" are a bit more verbose, telling you + what to do to add/remove your file permanently. + + * We no longer mess with /dev/tty in "commit" and "add". + + * More things are quiet with the -Q option set. + + * New src/config.h option: If CVS_BADROOT is set, CVS will not + allow people really logged in as "root" to commit changes. + + * "cvs diff" exits with a status of 0 if there were no diffs, 1 if + there were diffs, and 2 if there were errors. + + * "cvs -n diff" is now supported so that you can still run diffs + even while in the middle of committing files. + + * Handling of the CVS/Entries file is now much more robust. + + * The default file ignore list now includes "*.so". + + * "cvs import" did not expand '@' in the log message correctly. It + does now. Also, import now uses the ignore file facility + correctly. + + Import will now tell you whether there were conflicts that need to + be resolved, and how to resolve them. + + * "cvs log" has been changed so that you can "log" things that are + not a part of the current release (in the Attic). + + * If you don't change the editor message on commit, CVS now prompts + you with the choice: + + !)reuse this message unchanged for remaining dirs + + which allows you to tell CVS that you have no intention of changing + the log message for the remainder of the commit. + + * It is no longer necessary to have CVSROOT set if you are using + the -H option to get Usage information on the commands. + + * Command argument changes: + checkout: -P handling changed as described above. + New -j option (up to 2 can be specified) + for doing rcsmerge kind of things on + checkout. + commit: -r option now supports committing to a + numeric or symbolic tags, with some + restrictions. Full consistency checks will + be done. + Added "-f logfile" option, which tells + commit to glean the log message from the + specified file, rather than invoking the + editor. + rtag: Added -b option to create a branch tag, + useful for creating a patch for a previous + release, or for forking development. + tag: Added -b option to create a branch tag, + useful for creating a patch for a previous + release, or for forking development. + update: New -j option (up to 2 can be specified) + for doing rcsmerge kind of things on + update. + +Thu Jan 9 10:51:35 MST 1992 Jeff Polk (polk at BSDI.COM) + + * Changes between CVS 1.3 Beta-1 and CVS 1.3 Beta-2 + + * Thanks to K. Richard Pixley at Cygnus we now have function + prototypes in all the files + + * Some small changes to configure for portability. There have + been other portability problems submitted that have not been fixed + (Brian will be working on those). Additionally all __STDC__ + tests have been modified to check __STDC__ against the constant 1 + (this is what the Second edition of K&R says must be true). + + * Lots of additional error checking for forked processes (run_exec) + (thanks again to K. Richard Pixley) + + * Lots of miscellaneous bug fixes - including but certainly not + limited to: + various commit core dumps + various update core dumps + bogus results from status with numeric sticky tags + commitprog used freed memory + Entries file corruption caused by No_Difference + commit to revision broken (now works if branch exists) + ignore file processing broken for * and ! + ignore processing didn't handle memory reasonably + miscellaneous bugs in the recursion processor + file descriptor leak in ParseInfo + CVSROOT.adm->CVSROOT rename bug + lots of lint fixes + + * Reformatted all the code in src (with GNU indent) and then + went back and fixed prototypes, etc since indent gets confused. The + rationale is that it is better to do it sooner than later and now + everything is consistent and will hopefully stay that way. + The basic options to indent were: "-bad -bbb -bap -cdb -d0 -bl -bli0 + -nce -pcs -cs -cli4 -di1 -nbc -psl -lp -i4 -ip4 -c41" and then + miscellaneous formatting fixes were applied. Note also that the + "-nfc1" or "-nfca" may be appropriate in files where comments have + been carefully formatted (e.g, modules.c). + +Sat Dec 14 20:35:22 1991 Brian Berliner (berliner at sun.com) + + * Changes between CVS 1.2 and CVS 1.3 Beta are described here. + + * Lots of portability work. CVS now uses the GNU "configure" + script to dynamically determine the features provided by your + system. It probably is not foolproof, but it is better than + nothing. Please let me know of any portability problems. Some + file names were changed to fit within 14-characters. + + * CVS has a new RCS parser that is much more flexible and + extensible. It should read all known RCS ",v" format files. + + * Most of the commands now are fully recursive, rather than just + operating on the current directory alone. This includes "commit", + which makes it real easy to do an "atomic" commit of all the + changes made to a CVS hierarchy of sources. Most of the commands + also correctly handle file names that are in directories other than + ".", including absolute path names. Commands now accept the "-R" + option to force recursion on (though it is always the default now) + and the "-l" option to force recursion off, doing just "." and not + any sub-directories. + + * CVS supports many of the features provided with the RCS 5.x + distribution - including the new "-k" keyword expansion options. I + recommend using RCS 5.x (5.6 is the current official RCS version) + and GNU diff 1.15 (or later) distributions with CVS. + + * Checking out files with symbolic tags/dates is now "sticky", in + that CVS remembers the tag/date used for each file (and directory) + and will use that tag/date automatically on the next "update" call. + This stickyness also holds for files checked out with the the new + RCS 5.x "-k" options. + + * The "cvs diff" command now recognizes all of the rcsdiff 5.x + options. Unidiff format is available by installing the GNU + diff 1.15 distribution. + + * The old "CVS.adm" directories created on checkout are now called + "CVS" directories, to look more like "RCS" and "SCCS". Old CVS.adm + directories are automagically converted to CVS directories. The + old "CVSROOT.adm" directory within the source repository is + automagically changed into a "CVSROOT" directory as well. + + * Symbolic links in the source repository are fully supported ONLY + if you use RCS 5.6 or later and (of course) your system supports + symlinks. + + * A history database has been contributed which maintains the + history of certain CVS operations, as well as providing a wide array + of querying options. + + * The "cvs" program has a "-n" option which can be used with the + "update" command to show what would be updated without actually + doing the update, like: "cvs -n update". All usage statements + have been cleaned up and made more verbose. + + * The module database parsing has been rewritten. The new format + is compatible with the old format, but with much more + functionality. It allows modules to be created that grab pieces or + whole directories from various different parts of your source + repository. Module-relative specifications are also correctly + recognized now, like "cvs checkout module/file.c". + + * A configurable template can be specified such that on a "commit", + certain directories can supply a template that the user must fill + before completing the commit operation. + + * A configurable pre-commit checking program can be specified which + will run to verify that a "commit" can happen. This feature can be + used to restrict certain users from changing certain pieces of the + source repository, or denying commits to the entire source + repository. + + * The new "cvs export" command is much like "checkout", but + establishes defaults suitable for exporting code to others (expands + out keywords, forces the use of a symbolic tag, and does not create + "CVS" directories within the checked out sources. + + * The new "cvs import" command replaces the deprecated "checkin" + shell script and is used to import sources into CVS control. It is + also much faster for the first-time import. Some algorithmic + improvements have also been made to reduce the number of + conflicting files on next-time imports. + + * The new "cvs admin" command is basically an interface to the + "rcs" program. (Not yet implemented very well). + + * Signal handling (on systems with BSD or POSIX signals) is much + improved. Interrupting CVS now works with a single interrupt! + + * CVS now invokes RCS commands by direct fork/exec rather than + calling system(3). This improves performance by removing a call to + the shell to parse the arguments. + + * Support for the .cvsignore file has been contributed. CVS will + now show "unknown" files as "? filename" as the result of an "update" + command. The .cvsignore file can be used to add files to the + current list of ignored files so that they won't show up as unknown. + + * Command argument changes: + cvs: Added -l to turn off history logging. + Added -n to show what would be done without actually + doing anything. + Added -q/-Q for quiet and really quiet settings. + Added -t to show debugging trace. + add: Added -k to allow RCS 5.x -k options to be specified. + admin: New command; an interface to rcs(1). + checkout: Added -A to reset sticky tags/date/options. + Added -N to not shorten module paths. + Added -R option to force recursion. + Changed -p (prune empty directories) to -P option. + Changed -f option; forcing tags match is now default. + Added -p option to checkout module to standard output. + Added -s option to cat the modules db with status. + Added -d option to checkout in the specified directory. + Added -k option to use RCS 5.x -k support. + commit: Removed -a option; use -l instead. + Removed -f option. + Added -l option to disable recursion. + Added -R option to force recursion. + If no files specified, commit is recursive. + diff: Now recognizes all RCS 5.x rcsdiff options. + Added -l option to disable recursion. + Added -R option to force recursion. + history: New command; displays info about CVS usage. + import: Replaces "checkin" shell script; imports sources + under CVS control. Ignores files on the ignore + list (see -I option or .cvsignore description above). + export: New command; like "checkout", but w/special options + turned on by default to facilitate exporting sources. + join: Added -B option to join from base of the branch; + join now defaults to only joining with the top two + revisions on the branch. + Added -k option for RCS 5.x -k support. + log: Supports all RCS 5.x options. + Added -l option to disable recursion. + Added -R option to force recursion. + patch: Changed -f option; forcing tags match is now default. + Added -c option to force context-style diffs. + Added -u option to support unidiff-style diffs. + Added -V option to support RCS specific-version + keyword expansion formats. + Added -R option to force recursion. + remove: No option changes. It's a bit more verbose. + rtag: Equivalent to the old "cvs tag" command. + No option changes. It's a lot faster for re-tag. + status: New output formats with more information. + Added -l option to disable recursion. + Added -R option to force recursion. + Added -v option to show symbolic tags for files. + tag: Functionality changed to tag checked out files + rather than modules; use "rtag" command to get the + old "cvs tag" behaviour. + update: Added -A to reset sticky tags/date/options. + Changed -p (prune empty directories) to -P option. + Changed -f option; forcing tags match is now default. + Added -p option to checkout module to standard output. + Added -I option to add files to the ignore list. + Added -R option to force recursion. + + Major Contributors: + + * Jeff Polk rewrote most of the grody code of CVS + 1.2. He made just about everything dynamic (by using malloc), + added a generic hashed list manager, re-wrote the modules database + parsing in a compatible - but extended way, generalized directory + hierarchy recursion for virtually all the commands (including + commit!), generalized the loginfo file to be used for pre-commit + checks and commit templates, wrote a new and flexible RCS parser, + fixed an uncountable number of bugs, and helped in the design of + future CVS features. If there's anything gross left in CVS, it's + probably my fault! + + * David G. Grubbs contributed the CVS "history" and + "release" commands. As well as the ever-so-useful "-n" option of + CVS which tells CVS to show what it would do, without actually + doing it. He also contributed support for the .cvsignore file. + + * Paul Sander, HaL Computer Systems, Inc. wrote and + contributed the code in lib/sighandle.c. I added support for + POSIX, BSD, and non-POSIX/non-BSD systems. + + * Free Software Foundation contributed the "configure" script and + other compatibility support in the "lib" directory, which will help + make CVS much more portable. + + * Many others have contributed bug reports and enhancement requests. + Some have even submitted actual code which I have not had time yet + to integrate into CVS. Maybe for the next release. + + * Thanks to you all! + +Wed Feb 6 10:10:58 1991 Brian Berliner (berliner at sun.com) + + * Changes from CVS 1.0 Patchlevel 1 to CVS 1.0 Patchlevel 2; also + known as "Changes from CVS 1.1 to CVS 1.2". + + * Major new support with this release is the ability to use the + recently-posted RCS 5.5 distribution with CVS 1.2. See below for + other assorted bug-fixes that have been thrown in. + + * ChangeLog (new): Added Emacs-style change-log file to CVS 1.2 + release. Chronological description of changes between release. + + * README: Small fixes to installation instructions. My email + address is now "berliner@sun.com". + + * src/Makefile: Removed "rcstime.h". Removed "depend" rule. + + * src/partime.c: Updated to RCS 5.5 version with hooks for CVS. + * src/maketime.c: Updated to RCS 5.5 version with hooks for CVS. + * src/rcstime.h: Removed from the CVS 1.2 distribution. + Thanks to Paul Eggert for these changes. + + * src/checkin.csh: Support for RCS 5.5 parsing. + Thanks to Paul Eggert for this change. + + * src/collect_sets.c (Collect_Sets): Be quieter if "-f" option is + specified. When checking out files on-top-of other files that CVS + doesn't know about, run a diff in the hopes that they are really + the same file before aborting. + + * src/commit.c (branch_number): Fix for RCS 5.5 parsing. + Thanks to Paul Eggert for this change. + + * src/commit.c (do_editor): Bug fix - fprintf missing argument + which sometimes caused core dumps. + + * src/modules.c (process_module): Properly NULL-terminate + update_dir[] in all cases. + + * src/no_difference.c (No_Difference): The wrong RCS revision was + being registered in certain (strange) cases. + + * src/patch.c (get_rcsdate): New algorithm. No need to call + maketime() any longer. + Thanks to Paul Eggert for this change. + + * src/patchlevel.h: Increased patch level to "2". + + * src/subr.c (isdir, islink): Changed to compare stat mode bits + correctly. + + * src/tag.c (tag_file): Added support for following symbolic links + that are in the master source repository when tagging. Made tag + somewhat quieter in certain cases. + + * src/update.c (update_process_lists): Unlink the user's file if it + was put on the Wlist, meaning that the user's file is not modified + and its RCS file has been removed by someone else. + + * src/update.c (update): Support for "cvs update dir" to correctly + just update the argument directory "dir". + + * src/cvs.h: Fixes for RCS 5.5 parsing. + * src/version_number.c (Version_Number): Fixes for parsing RCS 5.5 + and older RCS-format files. + Thanks to Paul Eggert for these changes. + + * src/version_number.c (Version_Number): Bug fixes for "-f" option. + Bug fixes for parsing with certain branch numbers. RCS + revision/symbol parsing is much more solid now. + +Wed Feb 14 10:01:33 1990 Brian Berliner (berliner at sun.com) + + * Changes from CVS 1.0 Patchlevel 0 to CVS 1.0 Patchlevel 1; also + known as "Changes from CVS 1.0 to CVS 1.1". + + * src/patch.c (get_rcsdate): Portability fix. Replaced call to + timelocal() with call to maketime(). + +Mon Nov 19 23:15:11 1990 Brian Berliner (berliner at prisma.com) + + * Sent CVS 1.0 release to comp.sources.unix moderator and FSF. + + * Special thanks to Dick Grune for his work on the + 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. diff --git a/contrib/cvs-1.12.9/PROJECTS b/contrib/cvs-1.12.9/PROJECTS new file mode 100644 index 0000000000..b46eb2ab64 --- /dev/null +++ b/contrib/cvs-1.12.9/PROJECTS @@ -0,0 +1,53 @@ +This is a list of projects for CVS. In general, unlike the things in +the TODO file, these need more analysis to determine if and how +worthwhile each task is. + +I haven't gone through TODO, but it's likely that it has entries that +are actually more appropriate for this list. + +0. Improved Efficency + +* CVS uses a single doubly linked list/hash table data structure for + all of its lists. Since the back links are only used for deleting + list nodes it might be beneficial to use singly linked lists or a + tree structure. Most likely, a single list implementation will not + be appropriate for all uses. + + One easy change would be to remove the "type" field out of the list + and node structures. I have found it to be of very little use when + debugging, and each instance eats up a word of memory. This can add + up and be a problem on memory-starved machines. + + Profiles have shown that on fast machines like the Alpha, fsortcmp() + is one of the hot spots. + +* Dynamically allocated character strings are created, copied, and + destroyed throughout CVS. The overhead of malloc()/strcpy()/free() + needs to be measured. If significant, it could be minimized by using a + reference counted string "class". + +* File modification time is stored as a character string. It might be + worthwile to use a time_t internally if the time to convert a time_t + (from struct stat) to a string is greater that the time to convert a + ctime style string (from the entries file) to a time_t. time_t is + an machine-dependant type (although it's pretty standard on UN*X + systems), so we would have to have different conversion routines. + Profiles show that both operations are called about the same number + of times. + +* stat() is one of the largest performance bottlenecks on systems + without the 4.4BSD filesystem. By spliting information out of + the filesystem (perhaps the "rename database") we should be + able to improve performance. + +* Parsing RCS files is very expensive. This might be unnecessary if + RCS files are only used as containers for revisions, and tag, + revision, and date information was available in easy to read + (and modify) indexes. This becomes very apparent with files + with several hundred revisions. + +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-1.12.9/README b/contrib/cvs-1.12.9/README new file mode 100644 index 0000000000..ec46859602 --- /dev/null +++ b/contrib/cvs-1.12.9/README @@ -0,0 +1,117 @@ + CVS Kit + + Copyright (c) 1993-1994 Brian Berliner + Copyright (c) 1992 Brian Berliner and Jeff Polk + Copyright (c) 1989-1992, Brian Berliner + Copyright (c) 1998-2004 Free Software Foundation, + Derek Price, + & Ximbiot + All Rights Reserved + + 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 1, 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. + +------------------------------------------------------------------------------- + +Welcome to CVS! + +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). + +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 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 +release, but you are completely on your own. We recommend testing this +release on a source repository that is not critical to your work. THIS +SOFTWARE IS SUPPLIED COMPLETELY "AS IS". NO WARRANTY.... + +Thanks for your support! + + -The CVS Team + +------------------------------------------------------------------------------- + +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. + +------------------------------------------------------------------------------- + +Notes to people upgrading from a previous release of CVS: + +See the NEWS file for a description of features new in this version. + +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. + +------------------------------------------------------------------------------- + +Installation: + +Please read the INSTALL file for installation instructions. Brief summary: + + $ ./configure + $ make + (run the regression tests if desired) + $ make install + (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 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. + +------------------------------------------------------------------------------- + +* How do I get up-to-date information and information about other +versions of CVS? + +On the web, http://www.loria.fr/~molli/cvs-index.html. + +See also + http://www.cvshome.org + +The mailing list for CVS is info-cvs@gnu.org. Send +subscription and removal requests for that list to +info-cvs-request@gnu.org. + +The newsgroup for CVS (and other configuration management systems) is +comp.software.config-mgmt. There is not yet a CVS-specific newsgroup, +but perhaps if comp.software.config-mgmt gets enough CVS discussion, +then it will be possible to create one. + +------------------------------------------------------------------------------- + +Credits: See the AUTHORS file. diff --git a/contrib/cvs-1.12.9/README.DELETED b/contrib/cvs-1.12.9/README.DELETED new file mode 100644 index 0000000000..dceebe6822 --- /dev/null +++ b/contrib/cvs-1.12.9/README.DELETED @@ -0,0 +1,157 @@ +.cvsignore +ChangeLog +ChangeLog.zoo +FAQ +Makefile.am +Makefile.in +README.VMS +aclocal.m4 +build.com +compile +config.guess +config.h.in +config.rpath +config.sub +configure.in +contrib/.cvsignore +contrib/ChangeLog +contrib/Makefile.am +contrib/Makefile.in +contrib/debug_check_log.sh +contrib/descend.sh +contrib/dirfns.shar +contrib/newcvsroot.sh +contrib/pvcs2rcs.in +contrib/rcs2sccs.sh +contrib/sandbox_status.man +contrib/sandbox_status.sh +contrib/validate_repo.in +cvs-format.el +cvs.spec +cvs.spec.in +cvsnt.dep +cvsnt.dsp +cvsnt.dsw +cvsnt.mak +depcomp +diff/ChangeLog +diff/Makefile.am +diff/Makefile.in +diff/build_diff.com +diff/diagmeet.note +diff/libdiff.dep +diff/libdiff.dsp +diff/libdiff.mak +doc/.cvsignore +doc/ChangeLog +doc/ChangeLog.fsf +doc/Makefile.am +doc/Makefile.in +doc/RCSFILES +doc/cvs-paper.ms +doc/cvs-paper.ps +doc/cvs.info +doc/cvs.info-1 +doc/cvs.info-10 +doc/cvs.info-11 +doc/cvs.info-2 +doc/cvs.info-3 +doc/cvs.info-4 +doc/cvs.info-5 +doc/cvs.info-6 +doc/cvs.info-7 +doc/cvs.info-8 +doc/cvs.info-9 +doc/cvs.man.footer +doc/cvs.man.header +doc/cvs.ps +doc/cvsclient.info +doc/cvsclient.info-1 +doc/cvsclient.info-2 +doc/cvsclient.info-3 +doc/cvsclient.ps +doc/mdate-sh +doc/mkman.in +doc/stamp-1 +doc/stamp-vti +doc/texinfo.tex +emx +install-sh +lib/.cvsignore +lib/ChangeLog +lib/ChangeLog.fsf +lib/Makefile.am +lib/Makefile.in +lib/alloca_.h +lib/atexit.c +lib/build_lib.com +lib/dirname.c +lib/dup2.c +lib/fncase.c +lib/fnmatch.c +lib/fnmatch_.h +lib/fnmatch_loop.c +lib/fseeko.c +lib/ftello.c +lib/ftruncate.c +lib/getdate.c +lib/getdate.h +lib/gethostname.c +lib/getpagesize.h +lib/gettime.c +lib/gettimeofday.c +lib/libcvs.dep +lib/libcvs.dsp +lib/libcvs.mak +lib/malloc.c +lib/memmove.c +lib/mkdir.c +lib/mkstemp.c +lib/mktime.c +lib/nanosleep.c +lib/realloc.c +lib/regex.c +lib/rename.c +lib/stdbool_.h +lib/strerror.c +lib/strftime.c +lib/strstr.c +lib/strtoul.c +lib/tempname.c +lib/time_r.c +lib/time_r.h +lib/valloc.c +lib/waitpid.c +m4 +man/.cvsignore +man/ChangeLog +man/Makefile.am +man/Makefile.in +mdate-sh +missing +mkinstalldirs +mktemp.sh +os2 +src/.cvsignore +src/ChangeLog +src/ChangeLog-9194 +src/ChangeLog-9395 +src/ChangeLog-96 +src/ChangeLog-97 +src/Makefile.am +src/Makefile.in +src/build_src.com +src/gssapi-client.c +src/gssapi-client.h +src/kerberos4-client.c +src/kerberos4-client.h +src/sanity.config.sh.in +src/sanity.sh +tools/.cvsignore +tools/ChangeLog +tools/Makefile.am +tools/Makefile.in +vms +windows-NT +ylwrap +zlib diff --git a/contrib/cvs-1.12.9/README.DRAGONFLY b/contrib/cvs-1.12.9/README.DRAGONFLY new file mode 100644 index 0000000000..e0df75aaf8 --- /dev/null +++ b/contrib/cvs-1.12.9/README.DRAGONFLY @@ -0,0 +1,42 @@ + + CVS-1.12.9 AS USED BY DRAGONFLY + + This directory contains a selected set of files from the gnu + cvs-1.12.9.tar.gz distribution. No files have been moved + or modified from their extracted position. + + This distribution was downloaded from the following site: + + http://ccvs.cvshome.org/servlets/ProjectDownloadList + + DO NOT CREATE OR EDIT ANY FILES IN THIS DIRECTORY HIERARCHY! THIS + HIERARCHY REPRESENTS AN EXACT COPY, MINUS UNNEEDED FILES, OF THE + ORIGINAL ARCHIVE. All modifications are made in the + DragonFly build wrapper, in /usr/src/gnu/usr.bin/cvs, + by creating overrides or performing surgery on the distribution into + local files. The only additional files added to this directory + are README.DRAGONFLY and README.DELETED. + + UPGRADE PROCDURE: + + * download a new cvs-1.12.X dist greater then 1.12.9 + + * extract the archive into this directory, overlaying the + existing files. + + * A 'cvs update' will show you what has changed ('M') relative + to what we had before. There will be hundreds of files marked + '?' which, if not needed, should be deleted and NOT COMMITTED. + If any new files are needed you can cvs add and commit them. + + * Check overrides and patches in /usr/src/gnu/usr.bin/cvs and + modify as appropriate. + + DO NOT MAKE ANY EDITS TO THE DISTRIBUTION IN THIS CONTRIB + DIRECTORY, OTHER THEN TO ADD OR DELETE FILES ASSOCIATED WITH THE + GNU DISTRIBUTION. + + * Check to see if [src]/gnu/usr.bin/cvs/lib/config.h.proto needs + to be modified. + + The file README.DELETED contains a list of deleted files. diff --git a/contrib/cvs-1.12.9/TESTS b/contrib/cvs-1.12.9/TESTS new file mode 100644 index 0000000000..ddba947e27 --- /dev/null +++ b/contrib/cvs-1.12.9/TESTS @@ -0,0 +1,248 @@ +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 + +Also note that you must be logged in as a regular user, not root. + +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. + +The tests use a number of tools (awk, expr, id, tr, etc.) that are not +required for running CVS itself. In most cases, the standard vendor- +supplied versions of these tools work just fine, but there are some +exceptions -- expr in particular is heavily used and many vendor +versions are deficient in one way or another. Note that some vendors +provide multiple versions of tools (typically an ancient, traditional +version and a new, standards-conforming version), so you may already +have a usable version even if the default version isn't. If you don't +have a suitable tool, you can probably get one from the GNU Project (see +http://www.gnu.org). At this writting, expr and id are both part of the +GNU shellutils package, tr is part of the GNU textutils package, and awk +is part of the GNU gawk package. The test script tries to verify that +the tools exist and are usable; if not, it tries to find the GNU +versions and use them instead. If it can't find the GNU versions +either, it will print an error message and, depending on the severity of +the deficiency, it may exit. There are environment variables you can +set to use a particular version of a tool -- see the test script +(src/sanity.sh) for details. + +Some of the tests use fairly long command lines -- this usually isn't a +problem, but if you have a very short command line length limit (or a +lot of environment variables), you may run into trouble. Also, some of +the tests expect your local timezone to be an integral number of hours +from UTC -- if you usually use a fractional timezone, use a different +(integral) timezone when running the tests to avoid spurious failures. + +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; the debug_check_log +script in the contrib directory can assist in this process. + +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 + +It is worth thinking about how to: + +a. include spaces in arguments which we pass to the program under +test (sanity.sh dotest cannot do this; see test rcs-9 for a +workaround). + +b. pass stdin to the program under test (sanity.sh, again, handles +this by bypassing dotest). + +c. have a send-expect type dialog with the program under test + (e.g. see server-7 or pserver-4 which want to talk the CVS + protocol, or the many tests which need to answer the prompt of "cvs + release", e.g. deep-5). + +ABOUT ADDING YOUR OWN TESTS +*************************** + +As stated in the HACKING file, patches are not accepted without documentation +and tests. Many people seem to be scared off by the large size of the +sanity.sh script, but it is not really very complicated. + +You can probably ignore most of the begining of the script. This section +just sets some environment variables and finds the tools the script needs to +run. + +There is one main loop you can find by grepping for "The big loop". This loop +repeatedly calls a case statement where the individual cases are of the form: + + testname) + ... + ;; + +If you add a complete new test be sure to add it into the default list of tests +(grep for 'tests=' near the begining of the script) as well as the case +statement. During debugging, be aware that the sanity.sh usage allows for a '-f +testname' option to continue through the default list "from" a particular test +as well as interpreting everything in argv past the required options as test +names to run individual tests. + +Within each major test section, individual tests usually look like: + + dotest testname-subtestname "shell command" "optionally multiline regexp" + +Tests should always start in $testdir and create a subdirectory to operate in +and remove their cruft and end back in $testdir. The dotest functions output +failure messages and exit if the shell command exits with the wrong exit code or +its stdin/stderr output doesn't match the regexp. There are a few dotest +variations, most notably dotest_fail for expected non-zero exit codes. + +Other than that the script is mostly vanilla Bourne shell. There are a few +constructs used for versatility and portability. You can grep for the ones I +miss, but here are a few important ones. I'm leaving off long explanations +after the first few since it probably gives you the idea and the data is in +sanity.sh. + +Note that the boolean variables contain shell commands which return true or +false when executed and are intended to be used like, +"if $remote; then ... ; else ... ; fi" + + + * $testdir = the directory this test is taking place in + (CVSROOT=$testdir/cvsroot or CVSROOT=:fork:$testdir/cvsroot) + * $testcvs = full path to the cvs executable we are testing + * $PLUS = expr dependant uninterpreted '+' since this can vary + * $DOTSTAR = expr dependant _interpreted_ .* since some exprs don't match + EOL + * $username = regexp to match a username + * $hostname = regexp to match a hostname + * $SPROG = regexp to match server progname in CVS error messages. For + tests run in local and remote mode, this is usually the + string to test for, since some messages can be printed either + by the CVS client or CVS server, dependant on the mode. In + local mode it will = $PROG. + * $PROG = regexp to match client progname in CVS error messages. Use + this to match error messages known to be printed only from + the CVS client. + * $remote = ':' (true) or 'false', depending on whether the script is + running with a remote CVSROOT + * $keep = ':' (true) or 'false'. When set, the first test run will + leave any files and directories it created in $testdir and + exit when complete. + * $servercvs = cvs executable to be run as CVS_SERVER in remote testing + * $testcvs_server_support + = ':' (true) or 'false', depending whether server support was + detected in the ${testcvs} executable. + +And, of course, some characters like '.' in regexps need to be '\' escaped when +you mean them literally. Some characters may be interpreted by the shell, +e.g. backquotes and '$', are usually either escaped or replaced with '.'. +dotest adds the final '$' anchor to the regexp itself and all the expr +implementations I know of implicitly supply the start anchor ('^'). + +If you only make a few mistakes, the work is, of course, still usable, though we +may send the patch back to you for repair. :) diff --git a/contrib/cvs-1.12.9/TODO b/contrib/cvs-1.12.9/TODO new file mode 100644 index 0000000000..f536361d46 --- /dev/null +++ b/contrib/cvs-1.12.9/TODO @@ -0,0 +1,878 @@ +The "TODO" file! -*-Indented-Text-*- + +22. Catch signals for cleanup when "add"ing files. + +24. Insist on a log message. + (If done, this should be configurable via commitinfo or some new + config file -kingdon, Jun 1995). + +30. Add "rdiff" & "rtag" program options to the modules database. These + commands seem hard to use since these commands deal directly with the + RCS ,v files. (perhaps should think a little harder about what this is + trying to accomplish and what the best way is -kingdon, Jul 1997). + +31. Think hard about ^C recovery. + One particular issue: RCS removes the ,foo.c, file on ^C and CVS + doesn't. + +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 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. Take a look at PRCS 1.2. + PRCS 1.0 was particularly bad the way it handled the "invisible + state", but 1.2 is significantly better. + +49. cvs xxx commands should be able to deal with files in other + directories. I want to do a cvs add foo/bar.c. + [[ most commands now use the generic recursion processor, but not all; + this note is left here to remind me to fix the others ]] + +52. SCCS has a feature that I would *love* to see in CVS, as it is very + useful. One may make a private copy of SCCS suid to a particular user, + so other users in the authentication list may check files in and out of + a project directory without mucking about with groups. Is there any + plan to provide a similar functionality to CVS? Our site (and, I'd + imagine, many other sites with large user bases) has decided against + having the user-groups feature of unix available to the users, due to + perceived administrative, technical and performance headaches. A tool + such as CVS with features that provide group-like functionality would + be a huge help. + +62. Consider using revision controlled files and directories to handle the + new module format -- consider a cvs command front-end to + add/delete/modify module contents, maybe. + +63. The "import" and vendor support commands (co -j) need to be documented + better. + +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. + +72. Consider re-design of the module -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. + +79. Might be nice to have some sort of interface to Sun's Translucent + (?) File System and tagged revisions. + +82. Maybe the import stuff should allow an arbitrary revision to be + specified. + +84. Improve the documentation about administration of the repository and + how to add/remove files and the use of symbolic links. + +85. Make symbolic links a valid thing to put under version control. + Perhaps use one of the tag fields in the RCS file? Note that we + can only support symlinks that are relative and within the scope of + the sources being controlled. + +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 + procedure worked fine till it hit my echo, then silently aborted + leaving the lockfiles intact. Since I needn't use the loginfo + facility, I simply removed those commands and it all works. + +93. Need to think hard about release and development environments. Think + about execsets as well. + +98. If diff3 bombs out (too many differences) cvs then thinks that the file + has been updated and is OK to be commited even though the file + has not yet been merged. + +100. Checked out files should have revision control support. Maybe. + +102. Perhaps directory modes should be propagated on all import check-ins. + Not necessarily uid/gid changes. + +103. setuid/setgid on files is suspect. + +104. cvs should recover nicely on unreadable files/directories. + +105. cvs should have administrative tools to allow for changing permissions + and modes and what not. In particular, this would make cvs a + more attractive alternative to rdist. + +107. It should be possible to specify a list of symbolic revisions to + checkout such that the list is processed in reverse order looking for + matches within the RCS file for the symbolic revision. If there is + not a match, the next symbolic rev on the list is checked, and so on, + until all symbolic revs are exhausted. This would allow one to, say, + checkout "4.0" + "4.0.3" + "4.0.3Patch1" + "4.0.3Patch2" to get the + most recent 4.x stuff. This is usually handled by just specifying the + right release_tag, but most people forget to do this. + +108. If someone creates a whole new directory (i.e. adds it to the cvs + repository) and you happen to have a directory in your source farm by + the same name, when you do your cvs update -d it SILENTLY does + *nothing* to that directory. At least, I think it was silent; + certainly, it did *not* abort my cvs update, as it would have if the + same thing had happened with a file instead of a directory. + +109. I had gotten pieces of the sys directory in the past but not a + complete tree. I just did something like: + + cvs get * + + Where sys was in * and got the message + + cvs get: Executing 'sys/tools/make_links sys' + sh: sys/tools/make_links: not found + + I suspect this is because I didn't have the file in question, + but I do not understand how I could fool it into getting an + error. I think a later cvs get sys seemed to work so perhaps + something is amiss in handling multiple arguments to cvs get? + +113. The "cvs update" command should tee its output to a log file in ".". + (why? What is wrong with piping stdout to "tee"? -kingdon, Jun 1995) + +119. When importing a directory tree that is under SCCS/RCS control, + consider an option to have import checkout the SCCS/RCS files if + necessary. (This is if someone wants to import something which + is in RCS or SCCS without preserving the history, but makes 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 + (??? -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. + (See `importb' tests in src/sanity.sh for more details). + +124. Anyway, I thought you might want to add something like the following + to the cvs man pages: + + BUGS + The sum of the sizes of a module key and its contents are + limited. See ndbm(3). + +126. Do an analysis to see if CVS is forgetting to close file descriptors. + Especially when committing many files (more than the open file limit + for the particular UNIX). + +127. Look at *info files; they should all be quiet if the files are not + there. Should be able to point at a RCS directory and go. + +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. + + It should ParseEntries itself and access the entries list much like + Version_TS does (sticky tags and sticky options may need to be + supported here as well). Then it should only diff the things that + have the wrong time stamp (the ones that look modified). + +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. (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 + doing so, though, so it should be configurable in the .cvsrc file. + Also, along with this, we should look at the places where CVS itself + could be a little more synchronous so as not to lose data. + [[ I've done some of this, but it could use much more ]] + +138. Some people have suggested that CVS use a VPATH-like environment + variable to limit the amount of sources that need to be duplicated for + sites with giant source trees and no disk space. + +141. Import should accept modules as its directory argument. If we're + going to implement this, we should think hard about how modules + might be expanded and how to handle those cases. + +143. Update the documentation to show that the source repository is + something far away from the files that you work on. (People who + come from an RCS background are used to their `repository' being + _very_ close to their working directory.) + +144. Have cvs checkout look for the environment variable CVSPREFIX + (or CVSMODPREFIX or some such). If it's set, then when looking + up an alias in the modules database, first look it up with the + value of CVSPREFIX attached, and then look for the alias itself. + This would be useful when you have several projects in a single + repository. You could have aliases abc_src and xyz_src and + tell people working on project abc to put "setenv CVSPREFIX abc_" + in their .cshrc file (or equivalent for other shells). + Then they could do "cvs co src" to get a copy of their src + directory, not xyz's. (This should create a directory called + src, not abc_src.) + +145. After you create revision 1.1.1.1 in the previous scenario, if + you do "cvs update -r1 filename" you get revision 1.1, not + 1.1.1.1. It would be nice to get the later revision. Again, + this restriction comes from RCS and is probably hard to + change in CVS. Sigh. + + |"cvs update -r1 filename" does not tell RCS to follow any branches. CVS + |tries to be consistent with RCS in this fashion, so I would not change + |this. Within CVS we do have the flexibility of extending things, like + |making a revision of the form "-r1HEAD" find the most recent revision + |(branch or not) with a "1." prefix in the RCS file. This would get what + |you want maybe. + + This would be very useful. Though I would prefer an option + such as "-v1" rather than "-r1HEAD". This option might be + used quite often. + +146. The merging of files should be controlled via a hook so that programs + 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. Maybe there should be an option to cvs admin that allows a user to + change the Repository/Root file with some degree of error checking? + Something like "cvs admin reposmv /old/path /new/pretty/path". Before + it does the replace it check to see that the files + /new/pretty/path// exist. + + The obvious cases are where one moves the repository to another + machine or directory. But there are other cases, like where the + user might want to change from :pserver: to :ext:, use a different + server (if there are two server machines which share the + repository using a networked file system), etc. + + The status quo is a bit of a mess (as of, say, CVS 1.9). It is + that the -d global option has two moderately different uses. One + is to use a totally different repository (in which case we'd + probably want to give an error if it disagreed with CVS/Root, as + CVS 1.8 and earlier did). The other is the "reposmv" + functionality above (in which the two repositories really are the + same, and we want to update the CVS/Root files). In CVS 1.9 and + 1.10, -d rewrites the CVS/Root file (but not in subdirectories). + This behavior was not particularly popular and has been since + reverted. + + This whole area is a rather bad pile of individual decisions which + accumulated over time, some of them probably bad decisions with + hindsight. But we didn't get into this mess overnight, and we're + not going to get out of it overnight (that is, we need to come up + with a replacement behavior, document what parts of the status + quo are deprecated, probably circulate some unofficial patches, &c). + + (this item originally added 2 Feb 1992 but revised since). + +150. I have a customer request for a way to specify log message per + file, non-interactively before the commit, such that a single, fully + recursive commit prompts for one commit message, and concatenates the + per file messages for each file. In short, one commit, one editor + session, log messages allowed to vary across files within the commit. + Also, the per file messages should be allowed to be written when the + files are changed, which may predate the commit considerably. + + A new command seems appropriate for this. The state can be saved in the + CVS directory. I.e., + + % 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. (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). Also + would clean up the kludge for what to do with the message from + do_editor if the up-to-date check fails (see commit.c client code). + + 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). + + A few interesting issues with this: (1) if you do a cvs update or + some other operation which changes the working directory, do you + need to run "cvs message" again (it would, of course, bring up + the old message which you could accept)? Probably yes, after all + merging in some conflicts might change the situation. (2) How do + you change the stored messages if you change your mind before the + commit (probably run "cvs message" again, as hinted in (1))? + +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 + operation rather than a two step "cvs rtag -r Ulrtx_Build Ultrix_Build" + followed by "cvs rtag -d Ulrtx_Build" + +152. The "cvs -n" option does not work as one would expect for all the + commands. In particular, for "commit" and "import", where one would + also like to see what it would do, without actually doing anything. + +153. There should be some command (maybe I just haven't figured out + which one...) to import a source directory which is already + RCS-administered without losing all prior RCS gathered data. + Thus, it would have to examine the RCS files and choose a + starting version and branch higher than previous ones used. + (Check out rcs-to-cvs and see if it addresses this issue.) + +154. When committing the modules file, a pre-commit check should be done to + verify the validity of the new modules file before allowing it to be + committed. + +155. The options for "cvs history" are mutually exclusive, even though + useful queries can be done if they are not, as in specifying both + a module and a tag. A workaround is to specify the module, then + run the output through grep to only display lines that begin with + T, which are tag lines. (Better perhaps if we redesign the whole + "history" business -- check out doc/cvs.texinfo for the entire + rant.) + +156. Also, how hard would it be to allow continuation lines in the + {commit,rcs,log}info files? It would probably be useful with all of + the various flags that are now available, or if somebody has a lot of + files to put into a module. + +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 + first change, then overwrite that change with the second change. We + should catch this (typically unusual) case and issue an appropriate + diagnostic and die. + +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 + revision was already locked by the user as well, thus moving the lock + forward after the commit. + +163. The rtag/tag commands should have an option that removes the specified + tag from any file that is in the attic. This allows one to re-use a + tag (like "Mon", "Tue", ...) all the time and still have it tag the + real main-line code. + +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. + +166. There really needs to be a "Getting Started" document which describes + some of the new CVS philosophies. Folks coming straight from SCCS or + RCS might be confused by "cvs import". Also need to explain: + - How one might setup their $CVSROOT + - What all the tags mean in an "import" command + - Tags are important; revision numbers are not + +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 + a sanity check. + + For example, I've written a perl script which tells you what branch you're + on, if any. Hopefully this will help guard against mistaken checkins to + the trunk, or to the wrong branch. I suppose I can do this in + "commitinfo", but it'd be nice to advise people before they edit their + files. + + It would also be nice if there was some sort of "verboseness" switch to + the checkout and update commands that could turn this invocation of the + script off, for mature users. + +173. Need generic date-on-branch handling. Currently, many commands + allow both -r and -D, but that's problematic for commands like diff + that interpret that as two revisions rather than a single revision. + Checkout and update -j takes tag:date which is probably a better + solution overall. + +174. I would like to see "cvs release" modified so that it only removes files + which are known to CVS - all the files in the repository, plus those which + are listed in .cvsignore. This way, if you do leave something valuable in + a source tree you can "cvs release -d" the tree and your non-CVS goodies + are still there. If a user is going to leave non-CVS files in their source + trees, they really should have to clean them up by hand. + +175. And, in the feature request department, I'd dearly love a command-line + interface to adding a new module to the CVSROOT/modules file. + +176. If you use the -i flag in the modules file, you can control access + to source code; this is a Good Thing under certain circumstances. I + just had a nasty thought, and on experiment discovered that the + filter specified by -i is _not_ run before a cvs admin command; as + this allows a user to go behind cvs's back and delete information + (cvs admin -o1.4 file) this seems like a serious problem. + +177. We've got some external vendor source that sits under a source code + hierarchy, and when we do a cvs update, it gets wiped out because + its tag is different from the "main" distribution. I've tried to + use "-I" to ignore the directory, as well as .cvsignore, but this + doesn't work. + +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:bar" doesn't cut +it, because it erroneously shows the changes associated with the +change from the revision before foo to foo. I'm not sure that is ever +a useful or logical behavior ("cvs diff -r foo -r bar" gets this +right), but is compatibility an issue? See +http://www.cyclic.com/cvs/unoff-log.txt for an unofficial patch. + +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). + +186. There is a frequent discussion of multisite features. + +* There may be some overlap with the client/server CVS, which is good +especially when there is a single developer at each location. But by +"multisite" I mean something in which each site is more autonomous, to +one extent or another. + +* Vendor branches are the closest thing that CVS currently has for +multisite features. They have fixable drawbacks (such as poor +handling of added and removed files), and more fundamental drawbacks +(when you import a vendor branch, you are importing a set of files, +not importing any knowledge of their version history outside the +current repository). + +* One approach would be to require checkins (or other modifications to +the repository) to succeed at a write quorum of sites (51%) before +they are allowed to complete. To work well, the network should be +reliable enough that one can typically get to that many sites. When a +server which has been out of touch reconnects, it would want to update +its data before doing anything else. Any of the servers can service +all requests locally, except perhaps for a check that they are +up-to-date. The way this differs from a run-of-the-mill distributed +database is that if one only allows reversible operations via this +mechanism (exclude "cvs admin -o", "cvs tag -d", &c), then each site +can back up the others, such that failures at one site, including +something like deleting all the sources, can be recovered from. Thus +the sites need not trust each other as much as for many shared +databases, and the system may be resilient to many types of +organizational failures. Sometimes I call this design the +"CVScluster" design. + +* Another approach is a master/slave one. Checkins happen at the +master site, and slave sites need to check whether their local +repository is up to date before relying on its information. + +* Another approach is to have each site own a particular branch. This +one is the most tolerant of flaky networks; if checkins happen at each +site independently there is no particular problem. The big question +is whether merges happen only manually, as with existing CVS branches, +or whether there is a feature whereby there are circumstances in which +merges from one branch to the other happen automatically (for example, +the case in which the branches have not diverged). This might be a +legitimate question to ask even quite aside from multisite features. + +187. Might want to separate out usage error messages and help +messages. The problem now is that if you specify an invalid option, +for example, the error message is lost among all the help text. In +the new regime, the error message would be followed by a one-line +message directing people to the appropriate help option ("cvs -H +" or "cvs --help-commands" or whatever, according to the +situation). I'm not sure whether this change would be controversial +(as defined in HACKING), so there might be a need for further +discussion or other actions other than just coding. + +188. Option parsing and .cvsrc has at least one notable limitation. +If you want to set a global option only for some CVS commands, there +is no way to do it (for example, if one wants to set -q only for +"rdiff"). I am told that the "popt" package from RPM +(http://www.rpm.org) could solve this and other problems (for example, +if the syntax of option stuff in .cvsrc is similar to RPM, that would +be great from a user point of view). It would at least be worth a +look (it also provides a cleaner API than getopt_long). + +Another issue which may or may not be related is the issue of +overriding .cvsrc from the command line. The cleanest solution might +be to have options in mutually exclusive sets (-l/-R being a current +example, but --foo/--no-foo is a better way to name such options). Or +perhaps there is some better solution. + +189. Renaming files and directories is a frequently discussed topic. + +Some of the problems with the status quo: + +a. "cvs annotate" cannot operate on both the old and new files in a +single run. You need to run it twice, once for the new name and once +for the old name. + +b. "cvs diff" (or "cvs diff -N") shows a rename as a removal of the +old file and an addition of the new one. Some people would like to +see the differences between the file contents (but then how would we +indicate the fact that the file has been renamed? Certainly the +notion that "patch(1)" has of renames is as a removal and addition). + +c. "cvs log" should be able to show the changes between two +tags/dates, even in the presence of adds/removes/renames (I'm not sure +what the status quo is on this; see also item #182). + +d. Renaming directories is way too hard. + +Implementations: + +It is perhaps premature to try to design implementation details +without answering some of the above questions about desired behaviors +but several general implementations get mentioned. + +i. No fundamental changes (for example, a "cvs rename" command which +operated on directories could still implement the current recommended +practice for renaming directories, which is to rename each of the +files contained therein via an add and a remove). One thing to note +that the status quo gets right is proper merges, even with adds and +removals (Well, mostly right at least. There are a *LOT* of different +cases; see the testsuite for some of them). + +ii. Rename database. In this scheme the files in the repository +would have some arbitrary name, and then a separate rename database +would indicate the current correspondence between the filename in the +working directory and the actual storage. As far as I know this has +never been designed in detail for CVS. + +iii. A modest change in which the RCS files would contain some +information such as "renamed from X" or "renamed to Y". That is, this +would be generally similar to the log messages which are suggested +when one renames via an add and a removal, but would be +computer-parseable. I don't think anyone has tried to flesh out any +details here either. + +It is interesting to note that in solution ii. version numbers in the +"new file" start where the "old file" left off, while in solutions +i. and iii., version numbers restart from 1.1 each time a file is +renamed. Except perhaps in the case where we rename a file from foo +to bar and then back to foo. I'll shut up now. + +Regardless of the method we choose, we need to address how renames +affect existing CVS behaviors. For example, what happens when you +rename a file on a branch but not the trunk and then try to merge the +two? What happens when you rename a file on one branch and delete it +on another and try to merge the two? + +Ideally, we'd come up with a way to parameterize the problem and +simply write up a lookup table to determine the correct behavior. + +190. The meaning of the -q and -Q global options is very ad hoc; +there is no clear definition of which messages are suppressed by them +and which are not. Here is a classification of the current meanings +of -q; I don't know whether anyone has done a similar investigation of +-Q: + + a. The "warm fuzzies" printed upon entering each directory (for + example, "cvs update: Updating sdir"). The need for these messages + may be decreased now that most of CVS uses ->fullname instead of + ->file in messages (a project which is *still* not 100% complete, + alas). However, the issue of whether CVS can offer status as it + runs is an important one. Of course from the command line it is + hard to do this well and one ends up with options like -q. But + think about emacs, jCVS, or other environments which could flash you + the latest status line so you can see whether the system is working + or stuck. + + b. Other cases where the message just offers information (rather + than an error) and might be considered unnecessarily verbose. These + have a certain point to them, although it isn't really clear whether + it should be the same option as the warm fuzzies or whether it is + worth the conceptual hair: + + add.c: scheduling %s `%s' for addition (may be an issue) + modules.c: %s %s: Executing '%s' (I can see how that might be noise, + but...) + remove.c: scheduling `%s' for removal (analogous to the add.c one) + update.c: Checking out %s (hmm, that message is a bit on the noisy side...) + (but the similar message in annotate is not affected by -q). + + c. Suppressing various error messages. This is almost surely + bogus. + + commit.c: failed to remove tag `%s' from `%s' (Questionable. + Rationale might be that we already printed another message + elsewhere but why would it be necessary to avoid + the extra message in such an uncommon case?) + commit.c: failed to commit dead revision for `%s' (likewise) + remove.c: file `%s' still in working directory (see below about rm + -f analogy) + remove.c: nothing known about `%s' (looks dubious to me, especially in + the case where the user specified it explicitly). + remove.c: removed `%s' (seems like an obscure enough case that I fail + to see the appeal of being cryptically concise here). + remove.c: file `%s' already scheduled for removal (now it is starting + to look analogous to the infamous rm -f option). + rtag.c: cannot find tag `%s' in `%s' (more rm -f like behavior) + rtag.c: failed to remove tag `%s' from `%s' (ditto) + tag.c: failed to remove tag %s from %s (see above about whether RCS_* + has already printed an error message). + tag.c: couldn't tag added but un-commited file `%s' (more rm -f + like behavior) + tag.c: skipping removed but un-commited file `%s' (ditto) + tag.c: cannot find revision control file for `%s' (ditto, but at first + glance seems even worse, as this would seem to be a "can't happen" + condition) + +191. Storing RCS files, especially binary files, takes rather more +space than it could, typically. + - The virtue of the status quo is that it is simple to implement. + Of course it is also simplest in terms of dealing with compatibility. + - Just storing the revisions as separate gzipped files is a common + technique. It also is pretty simple (no new algorithms, CVS + already has zlib around). Of course for some files (such as files + which are already compressed) the gzip step won't help, but + something which can at least sometimes avoid rewriting the entire + RCS file for each new revision would, I would think, be a big + speedup for large files. + - Josh MacDonald has written a tool called xdelta which produces + differences (that is, sufficient information to transform the old + to the new) which looks for common sequences of bytes, like RCS + currently does, but which is not based on lines. This seems to do + quite well for some kinds of files (e.g. FrameMaker documents, + text files), and not as well for others (anything which is already + compressed, executables). xdelta 1.10 also is faster than GNU diff. + - Karl Fogel has thought some about using a difference technique + analogous to fractal compression (see the comp.compression FAQ for + more on fractal compression, including at least one patent to + watch for; I don't know how analogous Karl's ideas are to the + techniques described there). + - Quite possibly want some documented interface by which a site can + plug in their choice of external difference programs (with the + ability to choose the program based on filename, magic numbers, + or some such). + +192. "cvs update" using an absolute pathname does not work if the +working directory is not a CVS-controlled directory with the correct +CVSROOT. For example, the following will fail: + + cd /tmp + cvs -d /repos co foo + cd / + cvs update /tmp/foo + +It is possible to read the CVSROOT from the administrative files in +the directory specified by the absolute pathname argument to update. +In that case, the last command above would be equivalent to: + + cd /tmp/foo + cvs update . + +This can be problematic, however, if we ask CVS to update two +directories with different CVSROOTs. Currently, CVS has no way of +changing CVSROOT mid-stream. Consider the following: + + cd /tmp + cvs -d /repos1 co foo + cvs -d /repos2 co bar + cd / + cvs update /tmp/foo /tmp/bar + +To make that example work, we need to think hard about: + + - where and when CVSROOT-related variables get set + - who caches said variables for later use + - how the remote protocol should be extended to handle sending a new + repository mid-stream + - how the client should maintain connections to a variety of servers + in a single invocation. + +Because those issues are hairy, I suspect that having a change in +CVSROOT be an error would be a better move. + +193. The client relies on timestamps to figure out whether a file is +(maybe) modified. If something goes awry, then it ends up sending +entire files to the server to be checked, and this can be quite slow +especially over a slow network. A couple of things that can happen: +(a) other programs, like make, use timestamps, so one ends up needing +to do "touch foo" and otherwise messing with timestamps, (b) changing +the timezone offset (e.g. summer vs. winter or moving a machine) +should work on unix, but there may be problems with non-unix. + +Possible solutions: + + a. Store a checksum for each file in CVS/Entries or some such + place. What to do about hash collisions is interesting: using a + checksum, like MD5, large enough to "never" have collisions + probably works in practice (of course, if there is a collision then + all hell breaks loose because that code path was not tested, but + given the tiny, tiny probability of that I suppose this is only an + aesthetic issue). + + b. I'm not thinking of others, except storing the whole file in + CVS/Base, and I'm sure using twice the disk space would be + unpopular. + +194. CVS does not separate the "metadata" from the actual revision +history; it stores them both in the RCS files. Metadata means tags +and header information such as the number of the head revision. +Storing the metadata separately could speed up "cvs tag" enormously, +which is a big problem for large repositories. It could also probably +make CVS's locking much less in the way (see comment in do_recursion +about "two-pass design"). + +195. Many people using CVS over a slow link are interested in whether +the remote protocol could be any more efficient with network +bandwidth. This item is about one aspect of that--how the server +sends a new version of a file the client has a different version of, +or vice versa. + +a. Cases in which the status quo already sends a diff. For most text +files, this is probably already close to optimal. For binary files, +and anomalous (?) text files (e.g. those in which it would help to do +moves, as well as adds and deletes), it might be worth looking into other +difference algorithms (see item #191). + +b. Cases in which the status quo does not send a diff (e.g. "cvs +commit"). + +b1. With some frequency, people suggest rsync or a similar algorithm +(see ftp://samba.anu.edu.au/pub/rsync/). This could speed things up, +and in some ways involves the most minimal changes to the default CVS +paradigm. There are some downsides though: (1) there is an extra +network turnaround, (2) the algorithm needs to transmit some data to +discover what difference type programs can discover locally (although +this is only about 1% of the size of the files). + +b2. If one is willing to require that users use "cvs edit" before +editing a file on the client side (in some cases, a development +environment like emacs can make this fairly easy), then the Modified +request in the protocol could be extended to allow the client to just +send differences instead of entire files. In the degenerate case +(e.g. "cvs diff" without arguments) the required network traffic is +reduced to zero, and the client need not even contact the server. + +197. Analyze the difference between CVS_UNLINK & unlink_file. As far as I +can tell, unlink_file aborts in noexec mode and CVS_UNLINK does not. I'm not +sure it would be possible to remove even the use of temp files in noexec mode, +but most unlinks should probably be using unlink_file and not CVS_UNLINK. + +198. Remove references to deprecated cvs_temp_name function. + +199. Add test for login & logout functionality, including support for +backwards compatibility with old CVSROOTs. + +200. Make a 'cvs add' without write access a non-fatal error so that +the user's Entries file is updated and future 'cvs diffs' will work +properly. This should ease patch submission. + +201. cvs_temp_file should be creating temporary files in a privately owned +subdirectory of of temp due to security issues on some systems. + +202. Enable rdiff to accept most diff options. Make rdiff output look +like diff's. Make CVS diff garbage go to stderr and only standard diff +output go to stdout. + +203. Add val-tags additions to the tagging code. Don't remove the +update additions since val-tags could still be used as a cache when the +repository was imported from elsewhere (the tags weren't applied with a +version which wrote val-tags). + +204. Add test case for compression. A buf_shutdown error using compression +wasn't caught by the test suite. + +205. There are lots of cases where trailing slashes on directory names +and other non-canonical paths confuse CVS. Most of the cases that do +work are handled on an ad-hoc basis. We need to come up with a coherent +strategy to address path canonicalization and apply it consistently. + +208. Merge enhancements to the diff package back into the original GNU source. + +209. Go through this file and try to: + + a. Verify that items are still valid. + + b. Create test cases for valid items when they don't exist. + + c. Remove fixed and no longer applicable items. + +210. Explain to sanity.sh how to deal with paths with spaces and other odd +characters in them. + +211. Make sanity.sh run under the Win32 bash (cygwin) and maybe other Windex +environments (e.g. DGSS or whatever the MSVC portability environemnt is called). + +212. Autotestify (see autoconf source) sanity.sh. + +213. Examine desirability of updating the regex library (regex.{c,h}) to the +more recent versions that come with glibc and emacs. It might be worth waiting +for the emacs folks to get their act together and merge their changes into the +glibc version. + +214. Make options.h options configure script options instead. + +215. Add reditors and rwatchers commands. + + - Is an r* command abstraction layer possible here for the commands + where this makes sense? Would it be simpler? It seems to me the + major operational differences lie in the file list construction. + +218. Fix "checkout -d ." in client/server mode. + +221. Handle spaces in file/directory names. (Most, if not all, of the +internal infrastructure already handles them correctly, but most of the +administrative file interfaces do not.) + +223. Internationalization support. This probably means using some kind +of universal character set (ISO 10646?) internally and converting on +input and output, which opens the locale can of worms. + +225. Add support for --allow-root to server command. + +227. 'cvs release' should use the CVS/Root in the directory being released +when such is specified rather than $CVSROOT. In my work directory with no CVS +dir, a release of subdirectories causes the released projects to be tested +against my $CVSROOT environment variable, which often isn't correct but which +can complete without generating error messages if the project also exists in +the other CVSROOT. This happens a lot with my copies of the ccvs project. + +228. Consider adding -d to commit ala ci. + +229. Improve the locking code to use a random delay with exponential +backoff ala Ethernet and separate the notification interval from the +wait interval. + +230. Support for options like compression as part of the CVSROOT might be +nice. This should be fairly easy to implement now using the method options. + +231. The `cvs watch off' command needs an extension which enables users in the +cvsadmin group to turn watch off for users whose logins and email address may +not exist anymore. diff --git a/contrib/cvs-1.12.9/configure b/contrib/cvs-1.12.9/configure new file mode 100644 index 0000000000..f13cf3fd0e --- /dev/null +++ b/contrib/cvs-1.12.9/configure @@ -0,0 +1,33263 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.58 for Concurrent Versions System (CVS) 1.12.9. +# +# Report bugs to . +# +# Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, +# 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 +# Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='Concurrent Versions System (CVS)' +PACKAGE_TARNAME='cvs' +PACKAGE_VERSION='1.12.9' +PACKAGE_STRING='Concurrent Versions System (CVS) 1.12.9' +PACKAGE_BUGREPORT='bug-cvs@gnu.org' + +ac_unique_file="src/cvs.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +gl_header_list= +gl_func_list= +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO AMTAR install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM AWK SET_MAKE am__leading_dot ac_prefix_program MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CPP EGREP RANLIB ac_ct_RANLIB LN_S PERL CSH MKTEMP SENDMAIL PR ROFF PS2PDF TEXI2DVI MAKE_TARGETS_IN_VPATH_TRUE MAKE_TARGETS_IN_VPATH_FALSE LIBOBJS MKINSTALLDIRS USE_NLS MSGFMT GMSGFMT XGETTEXT MSGMERGE build build_cpu build_vendor build_os host host_cpu host_vendor host_os LIBICONV LTLIBICONV INTLLIBS LIBINTL LTLIBINTL POSUB STDBOOL_H HAVE__BOOL ALLOCA ALLOCA_H FNMATCH_H LIB_NANOSLEEP YACC YFLAGS LIB_CLOCK_GETTIME HAVE_PUTENV cvs_client_objects KRB4 ZLIB_SUBDIRS ZLIB_CPPFLAGS ZLIB_LIBS with_default_rsh RSH_DFLT EDITOR LTLIBOBJS' +ac_subst_files='MKTEMP_SH_FUNCTION' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP +ac_env_YACC_set=${YACC+set} +ac_env_YACC_value=$YACC +ac_cv_env_YACC_set=${YACC+set} +ac_cv_env_YACC_value=$YACC +ac_env_YFLAGS_set=${YFLAGS+set} +ac_env_YFLAGS_value=$YFLAGS +ac_cv_env_YFLAGS_set=${YFLAGS+set} +ac_cv_env_YFLAGS_value=$YFLAGS +ac_env_EDITOR_set=${EDITOR+set} +ac_env_EDITOR_value=$EDITOR +ac_cv_env_EDITOR_set=${EDITOR+set} +ac_cv_env_EDITOR_value=$EDITOR + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures Concurrent Versions System (CVS) 1.12.9 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of Concurrent Versions System (CVS) 1.12.9:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer + --disable-dependency-tracking Speeds up one-time builds + --enable-dependency-tracking Do not reject slow dependency extractors + --disable-largefile omit support for large files + --disable-nls do not use Native Language Support + --disable-rpath do not hardcode runtime library paths + --enable-cvs-ndbm Use the NDBM library distributed with CVS rather + than attempting to use a system NDBM library. + Disabling this may not work. (default) + --enable-client Include code for running as a remote client + (default) + --enable-password-authenticated-client + Enable pserver as a remote access method in the CVS + client (default) + --enable-server Include code for running as a server (default) + --enable-server-flow-control + 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 may + optionally pass a low water mark in bytes and a high + water mark in bytes, separated by commas. (default + is enabled 1M,2M) + --enable-pam Use to enable system authentication with PAM instead + of using the simple getpwnam interface. This allows + authentication (in theory) with any PAM module, e.g. + on systems with shadow passwords or via LDAP + --enable-case-sensitivity + Force CVS to expect a case sensitive file system. + Enabling this on a case insensitive system should + have little effect on the server or client + operation, though client users may ocassionally be + suprised that the CVS server appears to be case + sensitive. Disabling this for a case sensitive + server disables server support for case insensitive + clients, which can confuse all users of case + insensitive clients contacting the server. Disabling + this for a case sensitive client will cause the + client to ask servers to behave case insensitively, + which could cause confusion for users, but also + probably no real harm. (default autoselects based on + the case sensitivity of the file system containing + the current working directory) + --enable-encryption Enable encryption support (disabled by default) + --enable-force-editor 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, the -F 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 or -F. + --enable-force-editor will cause the -m or -F + message to be appended to the temp file when the + editor is started. (disabled by default) + --enable-lock-compatibility + Include locking code which prevents versions of CVS + earlier than 1.12.4 directly accessing the same + repositiory as this executable from ignoring this + executable's promotable read locks. If only CVS + versions 1.12.4 and later will be accessing your + repository directly (as a server or locally), you + can safely disable this option in return for fewer + disk accesses and a small speed increase. Disabling + this option when versions of CVS earlier than 1,12,4 + _will_ be accessing your repository, however, is + *VERY* *VERY* *VERY* dangerous and could result in + data loss. (enabled by default) + --enable-rootcommit Allow the root user to commit files (disabled by + default) + --enable-old-info-format-support + Enable support for the pre 1.12.1 *info scripting + hook format strings. Disable this option for a + smaller executable once your scripting hooks have + been updated to use the new *info format strings + (default). + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-gnu-ld assume the C compiler uses GNU ld default=no + --with-libiconv-prefix=DIR search for libiconv in DIR/include and DIR/lib + --without-libiconv-prefix don't search for libiconv in includedir and libdir + --with-libintl-prefix=DIR search for libintl in DIR/include and DIR/lib + --without-libintl-prefix don't search for libintl in includedir and libdir + --without-included-regex don't compile regex; this is the default on + systems with version 2 of the GNU C library + (use with caution on other system) + --with-krb4 Kerberos 4 directory (default /usr/kerberos) + --with-gssapi GSSAPI directory (default autoselects) + --with-external-zlib Use the specified ZLIB library (defaults to the + version distributed with the CVS source) + --with-rsh The default remote shell CVS will use for :ext: + transport (default rsh) + --with-editor The default text editor CVS should use for log + messages (default autoselects) + --with-hardcoded-pam-service-name + Use this to hard code a service name for PAM CVS + authentication. The special name, `program_name', + will cause CVS to use whatever name it was invoked + as as the service name. (defaults to `cvs') + --with-tmpdir The temporary directory CVS should use as a default + (default autoselects) + --with-umask Set the umask CVS will use by default in the + repository (default 002) + --with-cvs-admin-group=GROUP + The CVS admin command is restricted to the members + of this group. If this group does not exist, all + users are allowed to run CVS admin. To disable the + CVS admin command for all users, create an empty + group by specifying the --with-cvs-admin-group= + option. To disable access control for CVS admin, run + configure with the --without-cvs-admin-group option. + (default 'cvsadmin') + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + YACC The `Yet Another C Compiler' implementation to use. Defaults to + `bison -y'. Values other than `bison -y' will most likely break + on most systems. + YFLAGS YFLAGS contains the list arguments that will be passed by + default to Bison. This script will default YFLAGS to the empty + string to avoid a default value of `-d' given by some make + applications. + EDITOR The text editor CVS will use by default for log messages. + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +case "$ac_dir" in +.) ac_abs_builddir=$ac_builddir;; +*) + case $ac_builddir in + .) ac_abs_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_builddir=$ac_builddir;; + *) ac_abs_builddir="$ac_dir"/$ac_builddir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir="$ac_dir"/${ac_top_builddir}.;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir="$ac_dir"/$ac_srcdir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir="$ac_dir"/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +Concurrent Versions System (CVS) configure 1.12.9 +generated by GNU Autoconf 2.58 + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. + +Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, + 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by Concurrent Versions System (CVS) $as_me 1.12.9, which was +generated by GNU Autoconf 2.58. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +gl_header_list="$gl_header_list fcntl.h" +gl_header_list="$gl_header_list sys/time.h" +gl_header_list="$gl_header_list unistd.h" +gl_func_list="$gl_func_list mempcpy" +gl_func_list="$gl_func_list isascii" +gl_func_list="$gl_func_list gettimeofday" +gl_header_list="$gl_header_list sys/param.h" +gl_header_list="$gl_header_list wchar.h" +gl_header_list="$gl_header_list wctype.h" +gl_header_list="$gl_header_list stdlib.h" + + + + + + + + + + + + + + + + + + + + + + + + + + +am__api_version="1.7" +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +test "$program_prefix" != NONE && + program_transform_name="s,^,$program_prefix,;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$,$program_suffix,;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + + # test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='cvs' + VERSION='1.12.9' + + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +install_sh=${install_sh-"$am_aux_dir/install-sh"} + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STRIP=$ac_ct_STRIP +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. + + + + + +if test "x$prefix" = xNONE; then + echo $ECHO_N "checking for prefix by $ECHO_C" >&6 + # Extract the first word of "cvs", so it can be a program name with args. +set dummy cvs; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_ac_prefix_program+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $ac_prefix_program in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_prefix_program="$ac_prefix_program" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_prefix_program="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +ac_prefix_program=$ac_cv_path_ac_prefix_program + +if test -n "$ac_prefix_program"; then + echo "$as_me:$LINENO: result: $ac_prefix_program" >&5 +echo "${ECHO_T}$ac_prefix_program" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + if test -n "$ac_prefix_program"; then + prefix=`(dirname "$ac_prefix_program") 2>/dev/null || +$as_expr X"$ac_prefix_program" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_prefix_program" : 'X\(//\)[^/]' \| \ + X"$ac_prefix_program" : 'X\(//\)$' \| \ + X"$ac_prefix_program" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_prefix_program" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + prefix=`(dirname "$prefix") 2>/dev/null || +$as_expr X"$prefix" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$prefix" : 'X\(//\)[^/]' \| \ + X"$prefix" : 'X\(//\)$' \| \ + X"$prefix" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$prefix" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + fi +fi + + ac_config_headers="$ac_config_headers config.h" + +echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6 + # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then + enableval="$enable_maintainer_mode" + USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi; + echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5 +echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6 + + +if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + + +cat >>confdefs.h <<\_ACEOF +#define _GNU_SOURCE 1 +_ACEOF + + +DEPDIR="${am__leading_dot}deps" + + ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi + + +echo "$as_me:$LINENO: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval="$enable_dependency_tracking" + +fi; +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + : > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # (even with -Werror). So we grep stderr for any message + # that says an option was ignored. + if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6 +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + + +echo "$as_me:$LINENO: checking for AIX" >&5 +echo $ECHO_N "checking for AIX... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef _AIX + yes +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +cat >>confdefs.h <<\_ACEOF +#define _ALL_SOURCE 1 +_ACEOF + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +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 <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +if test "${ac_cv_header_minix_config_h+set}" = set; then + echo "$as_me:$LINENO: checking for minix/config.h" >&5 +echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6 +if test "${ac_cv_header_minix_config_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking minix/config.h usability" >&5 +echo $ECHO_N "checking minix/config.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking minix/config.h presence" >&5 +echo $ECHO_N "checking minix/config.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: minix/config.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: minix/config.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: minix/config.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: minix/config.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: minix/config.h: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for minix/config.h" >&5 +echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6 +if test "${ac_cv_header_minix_config_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_minix_config_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6 + +fi +if test $ac_cv_header_minix_config_h = yes; then + MINIX=yes +else + MINIX= +fi + + +if test "$MINIX" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_SOURCE 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_1_SOURCE 2 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _MINIX 1 +_ACEOF + +fi + + + + + + + + + + + + cat >>confdefs.h <<\_ACEOF +#define __EXTENSIONS__ 1 +_ACEOF + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + : > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # (even with -Werror). So we grep stderr for any message + # that says an option was ignored. + if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6 +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +if test "x$CC" != xcc; then + echo "$as_me:$LINENO: checking whether $CC and cc understand -c and -o together" >&5 +echo $ECHO_N "checking whether $CC and cc understand -c and -o together... $ECHO_C" >&6 +else + echo "$as_me:$LINENO: checking whether cc understands -c and -o together" >&5 +echo $ECHO_N "checking whether cc understands -c and -o together... $ECHO_C" >&6 +fi +set dummy $CC; ac_cc=`echo $2 | + sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if eval "test \"\${ac_cv_prog_cc_${ac_cc}_c_o+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +# Make sure it works both with $CC and with simple cc. +# We do the test twice because some compilers refuse to overwrite an +# existing .o file with -o, though they will create one. +ac_try='$CC -c conftest.$ac_ext -o conftest.$ac_objext >&5' +if { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest.$ac_objext && { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; +then + eval ac_cv_prog_cc_${ac_cc}_c_o=yes + if test "x$CC" != xcc; then + # Test first that cc exists at all. + if { ac_try='cc -c conftest.$ac_ext >&5' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_try='cc -c conftest.$ac_ext -o conftest.$ac_objext >&5' + if { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest.$ac_objext && { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; + then + # cc works too. + : + else + # cc exists but doesn't like -o. + eval ac_cv_prog_cc_${ac_cc}_c_o=no + fi + fi + fi +else + eval ac_cv_prog_cc_${ac_cc}_c_o=no +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define NO_MINUS_C_MINUS_O 1 +_ACEOF + +fi + +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +ac_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" != yes"; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi + + +# Find the posix library needed on INTERACTIVE UNIX (ISC) +echo "$as_me:$LINENO: checking for library containing strerror" >&5 +echo $ECHO_N "checking for library containing strerror... $ECHO_C" >&6 +if test "${ac_cv_search_strerror+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_strerror=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strerror (); +int +main () +{ +strerror (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_strerror="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_strerror" = no; then + for ac_lib in cposix; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strerror (); +int +main () +{ +strerror (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_strerror="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_strerror" >&5 +echo "${ECHO_T}$ac_cv_search_strerror" >&6 +if test "$ac_cv_search_strerror" != no; then + test "$ac_cv_search_strerror" = "none required" || LIBS="$ac_cv_search_strerror $LIBS" + +fi + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +echo "$as_me:$LINENO: checking whether ln -s works" >&5 +echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6 +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no, using $LN_S" >&5 +echo "${ECHO_T}no, using $LN_S" >&6 +fi + +# Check whether --enable-largefile or --disable-largefile was given. +if test "${enable_largefile+set}" = set; then + enableval="$enable_largefile" + +fi; +if test "$enable_largefile" != no; then + + echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5 +echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_largefile_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext + CC="$CC -n32" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_largefile_CC=' -n32'; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5 +echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6 + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_file_offset_bits+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_file_offset_bits=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_file_offset_bits=64; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5 +echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6 +if test "$ac_cv_sys_file_offset_bits" != no; then + +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF + +fi +rm -f conftest* + echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5 +echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_large_files+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_large_files=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_large_files=1; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5 +echo "${ECHO_T}$ac_cv_sys_large_files" >&6 +if test "$ac_cv_sys_large_files" != no; then + +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF + +fi +rm -f conftest* +fi + + + +# Extract the first word of "perl", so it can be a program name with args. +set dummy perl; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PERL+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PERL in + [\\/]* | ?:[\\/]*) + ac_cv_path_PERL="$PERL" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_PERL" && ac_cv_path_PERL="no" + ;; +esac +fi +PERL=$ac_cv_path_PERL + +if test -n "$PERL"; then + echo "$as_me:$LINENO: result: $PERL" >&5 +echo "${ECHO_T}$PERL" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +# Extract the first word of "csh", so it can be a program name with args. +set dummy csh; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_CSH+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $CSH in + [\\/]* | ?:[\\/]*) + ac_cv_path_CSH="$CSH" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_CSH="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_CSH" && ac_cv_path_CSH="no" + ;; +esac +fi +CSH=$ac_cv_path_CSH + +if test -n "$CSH"; then + echo "$as_me:$LINENO: result: $CSH" >&5 +echo "${ECHO_T}$CSH" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +# for contrib/rcs2log.sh & src/cvsbug.in. +# Extract the first word of "mktemp", so it can be a program name with args. +set dummy mktemp; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_MKTEMP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $MKTEMP in + [\\/]* | ?:[\\/]*) + ac_cv_path_MKTEMP="$MKTEMP" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_MKTEMP="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_MKTEMP" && ac_cv_path_MKTEMP="mktemp" + ;; +esac +fi +MKTEMP=$ac_cv_path_MKTEMP + +if test -n "$MKTEMP"; then + echo "$as_me:$LINENO: result: $MKTEMP" >&5 +echo "${ECHO_T}$MKTEMP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if test x"$MKTEMP" = xmktemp; then + MKTEMP_SH_FUNCTION=$srcdir/mktemp.sh +else + MKTEMP_SH_FUNCTION=/dev/null +fi + +# for src/cvsbug.in +# Extract the first word of "sendmail", so it can be a program name with args. +set dummy sendmail; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_SENDMAIL+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $SENDMAIL in + [\\/]* | ?:[\\/]*) + ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/sbin:/usr/lib" +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL="no" + ;; +esac +fi +SENDMAIL=$ac_cv_path_SENDMAIL + +if test -n "$SENDMAIL"; then + echo "$as_me:$LINENO: result: $SENDMAIL" >&5 +echo "${ECHO_T}$SENDMAIL" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +# For diff/util.c +# Extract the first word of "pr", so it can be a program name with args. +set dummy pr; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PR in + [\\/]* | ?:[\\/]*) + ac_cv_path_PR="$PR" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PR="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_PR" && ac_cv_path_PR="no" + ;; +esac +fi +PR=$ac_cv_path_PR + +if test -n "$PR"; then + echo "$as_me:$LINENO: result: $PR" >&5 +echo "${ECHO_T}$PR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if test x"$PR" != xno; then + +cat >>confdefs.h <<_ACEOF +#define PR_PROGRAM "$PR" +_ACEOF + +fi + +missing_dir=`cd $ac_aux_dir && pwd` +glocs="$PATH:/usr/local/bin:/usr/contrib/bin:/usr/gnu/bin:/local/bin:/local/gnu/bin:/gnu/bin" +for ac_prog in groff roff +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_ROFF+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $ROFF in + [\\/]* | ?:[\\/]*) + ac_cv_path_ROFF="$ROFF" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $glocs +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ROFF="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +ROFF=$ac_cv_path_ROFF + +if test -n "$ROFF"; then + echo "$as_me:$LINENO: result: $ROFF" >&5 +echo "${ECHO_T}$ROFF" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ROFF" && break +done +test -n "$ROFF" || ROFF="$missing_dir/missing roff" + +# Extract the first word of "ps2pdf", so it can be a program name with args. +set dummy ps2pdf; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PS2PDF+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PS2PDF in + [\\/]* | ?:[\\/]*) + ac_cv_path_PS2PDF="$PS2PDF" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PS2PDF="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_PS2PDF" && ac_cv_path_PS2PDF="$missing_dir/missing ps2pdf" + ;; +esac +fi +PS2PDF=$ac_cv_path_PS2PDF + +if test -n "$PS2PDF"; then + echo "$as_me:$LINENO: result: $PS2PDF" >&5 +echo "${ECHO_T}$PS2PDF" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +# Extract the first word of "texi2dvi", so it can be a program name with args. +set dummy texi2dvi; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_TEXI2DVI+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $TEXI2DVI in + [\\/]* | ?:[\\/]*) + ac_cv_path_TEXI2DVI="$TEXI2DVI" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_TEXI2DVI="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_TEXI2DVI" && ac_cv_path_TEXI2DVI="$missing_dir/missing texi2dvi" + ;; +esac +fi +TEXI2DVI=$ac_cv_path_TEXI2DVI + +if test -n "$TEXI2DVI"; then + echo "$as_me:$LINENO: result: $TEXI2DVI" >&5 +echo "${ECHO_T}$TEXI2DVI" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + +echo "$as_me:$LINENO: checking whether #! works in shell scripts" >&5 +echo $ECHO_N "checking whether #! works in shell scripts... $ECHO_C" >&6 +if test "${ac_cv_sys_interpreter+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + echo '#! /bin/cat +exit 69 +' >conftest +chmod u+x conftest +(SHELL=/bin/sh; export SHELL; ./conftest >/dev/null) +if test $? -ne 69; then + ac_cv_sys_interpreter=yes +else + ac_cv_sys_interpreter=no +fi +rm -f conftest +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_interpreter" >&5 +echo "${ECHO_T}$ac_cv_sys_interpreter" >&6 +interpval=$ac_cv_sys_interpreter + +if test X"$ac_cv_sys_interpreter" != X"yes" ; then + # silly trick to avoid problems in AC macros... + ac_msg='perl scripts using #! may not be invoked properly' + { echo "$as_me:$LINENO: WARNING: $ac_msg" >&5 +echo "$as_me: WARNING: $ac_msg" >&2;} +fi + +# BSD's logo is a devil for a reason, hey? +echo "$as_me:$LINENO: checking for BSD VPATH bug in make" >&5 +echo $ECHO_N "checking for BSD VPATH bug in make... $ECHO_C" >&6 +if test "${ccvs_cv_bsd_make_vpath_bug+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test ! -d ac_test_dir ; then + { ac_try='mkdir ac_test_dir' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } +fi +cat >conftestmake <&2 +ac_test_dep: ac_test_dep_dep +EOF +touch ac_test_dir/ac_test_dep_dep +touch ac_test_dir/ac_test_dep +touch ac_test_target +# Don't know why, but the following test doesn't work under FreeBSD 4.2 +# without this sleep command +sleep 1 +if { ac_try='make -f conftestmake 2>&1 >/dev/null |grep ^BSD\ VPATH\ bug\ present\$ >/dev/null' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } ; then + ccvs_cv_bsd_make_vpath_bug=yes +else + ccvs_cv_bsd_make_vpath_bug=no +fi +{ ac_try='rm -rf ac_test_dir ac_test_target conftestmake' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } +fi +echo "$as_me:$LINENO: result: $ccvs_cv_bsd_make_vpath_bug" >&5 +echo "${ECHO_T}$ccvs_cv_bsd_make_vpath_bug" >&6 +# We also don't need to worry about the bug when $srcdir = $builddir + + +if \ + test $ccvs_cv_bsd_make_vpath_bug = no \ + || test $srcdir = .; then + MAKE_TARGETS_IN_VPATH_TRUE= + MAKE_TARGETS_IN_VPATH_FALSE='#' +else + MAKE_TARGETS_IN_VPATH_TRUE='#' + MAKE_TARGETS_IN_VPATH_FALSE= +fi + + + + + + + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5 +echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_hdr> + +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +_ACEOF + +ac_header_dirent=$ac_hdr; break +fi + +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then + echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_opendir=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_opendir" = no; then + for ac_lib in dir; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6 +if test "$ac_cv_search_opendir" != no; then + test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" + +fi + +else + echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_opendir=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_opendir" = no; then + for ac_lib in x; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6 +if test "$ac_cv_search_opendir" != no; then + test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" + +fi + +fi + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +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 <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 +if test "${ac_cv_header_sys_wait_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_sys_wait_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_sys_wait_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + + + + + + + + + + + + + + + +for ac_header in \ + direct.h \ + fcntl.h \ + io.h \ + memory.h \ + ndbm.h \ + stdint.h \ + syslog.h \ + sys/bsdtypes.h \ + sys/file.h \ + sys/param.h \ + sys/resource.h \ + sys/select.h \ + unistd.h \ + utime.h\ + +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking whether stat file-mode macros are broken" >&5 +echo $ECHO_N "checking whether stat file-mode macros are broken... $ECHO_C" >&6 +if test "${ac_cv_header_stat_broken+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +#if defined(S_ISBLK) && defined(S_IFDIR) +# if S_ISBLK (S_IFDIR) +You lose. +# endif +#endif + +#if defined(S_ISBLK) && defined(S_IFCHR) +# if S_ISBLK (S_IFCHR) +You lose. +# endif +#endif + +#if defined(S_ISLNK) && defined(S_IFREG) +# if S_ISLNK (S_IFREG) +You lose. +# endif +#endif + +#if defined(S_ISSOCK) && defined(S_IFREG) +# if S_ISSOCK (S_IFREG) +You lose. +# endif +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "You lose" >/dev/null 2>&1; then + ac_cv_header_stat_broken=yes +else + ac_cv_header_stat_broken=no +fi +rm -f conftest* + +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stat_broken" >&5 +echo "${ECHO_T}$ac_cv_header_stat_broken" >&6 +if test $ac_cv_header_stat_broken = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STAT_MACROS_BROKEN 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 +echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6 +if test "${ac_cv_type_uid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 +echo "${ECHO_T}$ac_cv_type_uid_t" >&6 +if test $ac_cv_type_uid_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uid_t int +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define gid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for mode_t" >&5 +echo $ECHO_N "checking for mode_t... $ECHO_C" >&6 +if test "${ac_cv_type_mode_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((mode_t *) 0) + return 0; +if (sizeof (mode_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_mode_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_mode_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_mode_t" >&5 +echo "${ECHO_T}$ac_cv_type_mode_t" >&6 +if test $ac_cv_type_mode_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define mode_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_signal=int +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + +echo "$as_me:$LINENO: checking for struct stat.st_blksize" >&5 +echo $ECHO_N "checking for struct stat.st_blksize... $ECHO_C" >&6 +if test "${ac_cv_member_struct_stat_st_blksize+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (ac_aggr.st_blksize) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_blksize=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (sizeof ac_aggr.st_blksize) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_blksize=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_stat_st_blksize=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_blksize" >&5 +echo "${ECHO_T}$ac_cv_member_struct_stat_st_blksize" >&6 +if test $ac_cv_member_struct_stat_st_blksize = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_BLKSIZE 1 +_ACEOF + + +fi + +echo "$as_me:$LINENO: checking for struct stat.st_rdev" >&5 +echo $ECHO_N "checking for struct stat.st_rdev... $ECHO_C" >&6 +if test "${ac_cv_member_struct_stat_st_rdev+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (ac_aggr.st_rdev) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_rdev=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (sizeof ac_aggr.st_rdev) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_rdev=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_stat_st_rdev=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_rdev" >&5 +echo "${ECHO_T}$ac_cv_member_struct_stat_st_rdev" >&6 +if test $ac_cv_member_struct_stat_st_rdev = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_RDEV 1 +_ACEOF + + +fi + + +echo "$as_me:$LINENO: checking for _LARGEFILE_SOURCE value needed for large files" >&5 +echo $ECHO_N "checking for _LARGEFILE_SOURCE value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_largefile_source+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_largefile_source=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +return !fseeko; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _LARGEFILE_SOURCE 1 +#include +int +main () +{ +return !fseeko; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_largefile_source=1; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_source" >&5 +echo "${ECHO_T}$ac_cv_sys_largefile_source" >&6 +if test "$ac_cv_sys_largefile_source" != no; then + +cat >>confdefs.h <<_ACEOF +#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source +_ACEOF + +fi +rm -f conftest* + +# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug +# in glibc 2.1.3, but that breaks too many other things. +# If you want fseeko and ftello with glibc, upgrade to a fixed glibc. +echo "$as_me:$LINENO: checking for fseeko" >&5 +echo $ECHO_N "checking for fseeko... $ECHO_C" >&6 +if test "${ac_cv_func_fseeko+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +return fseeko && fseeko (stdin, 0, 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fseeko=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_fseeko=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fseeko" >&5 +echo "${ECHO_T}$ac_cv_func_fseeko" >&6 +if test $ac_cv_func_fseeko = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_FSEEKO 1 +_ACEOF + +fi + +if test $ac_cv_func_fseeko = no; then + case $LIBOBJS in + "fseeko.$ac_objext" | \ + *" fseeko.$ac_objext" | \ + "fseeko.$ac_objext "* | \ + *" fseeko.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS fseeko.$ac_objext" ;; +esac + + case $LIBOBJS in + "ftello.$ac_objext" | \ + *" ftello.$ac_objext" | \ + "ftello.$ac_objext "* | \ + *" ftello.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS ftello.$ac_objext" ;; +esac + +fi + +# Replace functions with versions in lib/ when they can't be found. + + + + + + + + +for ac_func in \ + dup2 \ + ftruncate \ + mkdir \ + rename \ + strstr \ + strtoul\ + valloc \ + waitpid \ + +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + + + +# +# Begin GNULIB stuff. +# + +# Look for functions from GNULIB and replace with versions in lib/ when +# necessary. +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + + + + echo "$as_me:$LINENO: checking whether gettimeofday clobbers localtime buffer" >&5 +echo $ECHO_N "checking whether gettimeofday clobbers localtime buffer... $ECHO_C" >&6 +if test "${jm_cv_func_gettimeofday_clobber+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + jm_cv_func_gettimeofday_clobber=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include + +int +main () +{ + time_t t = 0; + struct tm *lt; + struct tm saved_lt; + struct timeval tv; + lt = localtime (&t); + saved_lt = *lt; + gettimeofday (&tv, NULL); + if (memcmp (lt, &saved_lt, sizeof (struct tm)) != 0) + exit (1); + + exit (0); +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + jm_cv_func_gettimeofday_clobber=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +jm_cv_func_gettimeofday_clobber=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + +fi +echo "$as_me:$LINENO: result: $jm_cv_func_gettimeofday_clobber" >&5 +echo "${ECHO_T}$jm_cv_func_gettimeofday_clobber" >&6 + if test $jm_cv_func_gettimeofday_clobber = yes; then + + case $LIBOBJS in + "gettimeofday.$ac_objext" | \ + *" gettimeofday.$ac_objext" | \ + "gettimeofday.$ac_objext "* | \ + *" gettimeofday.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS gettimeofday.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define gmtime rpl_gmtime +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define localtime rpl_localtime +_ACEOF + + + + +cat >>confdefs.h <<\_ACEOF +#define gettimeofday rpl_gettimeofday +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define GETTIMEOFDAY_CLOBBERS_LOCALTIME_BUFFER 1 +_ACEOF + + + + + fi + +echo "$as_me:$LINENO: checking whether strerror_r is declared" >&5 +echo $ECHO_N "checking whether strerror_r is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_strerror_r+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef strerror_r + char *p = (char *) strerror_r; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_strerror_r=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_strerror_r=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_strerror_r" >&5 +echo "${ECHO_T}$ac_cv_have_decl_strerror_r" >&6 +if test $ac_cv_have_decl_strerror_r = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRERROR_R 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRERROR_R 0 +_ACEOF + + +fi + + + +for ac_func in strerror_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking whether strerror_r returns char *" >&5 +echo $ECHO_N "checking whether strerror_r returns char *... $ECHO_C" >&6 +if test "${ac_cv_func_strerror_r_char_p+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + ac_cv_func_strerror_r_char_p=no + if test $ac_cv_have_decl_strerror_r = yes; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + char buf[100]; + char x = *strerror_r (0, buf, sizeof buf); + char *p = strerror_r (0, buf, sizeof buf); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_strerror_r_char_p=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + else + # strerror_r is not declared. Choose between + # systems that have relatively inaccessible declarations for the + # function. BeOS and DEC UNIX 4.0 fall in this category, but the + # former has a strerror_r that returns char*, while the latter + # has a strerror_r that returns `int'. + # This test should segfault on the DEC system. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + extern char *strerror_r (); +int +main () +{ +char buf[100]; + char x = *strerror_r (0, buf, sizeof buf); + exit (!isalpha (x)); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_strerror_r_char_p=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + fi + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_strerror_r_char_p" >&5 +echo "${ECHO_T}$ac_cv_func_strerror_r_char_p" >&6 +if test $ac_cv_func_strerror_r_char_p = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STRERROR_R_CHAR_P 1 +_ACEOF + +fi + + + echo "$as_me:$LINENO: checking for ssize_t" >&5 +echo $ECHO_N "checking for ssize_t... $ECHO_C" >&6 +if test "${gt_cv_ssize_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +int x = sizeof (ssize_t *) + sizeof (ssize_t); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gt_cv_ssize_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gt_cv_ssize_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $gt_cv_ssize_t" >&5 +echo "${ECHO_T}$gt_cv_ssize_t" >&6 + if test $gt_cv_ssize_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define ssize_t int +_ACEOF + + fi + + + + + am_getline_needs_run_time_check=no + echo "$as_me:$LINENO: checking for getline" >&5 +echo $ECHO_N "checking for getline... $ECHO_C" >&6 +if test "${ac_cv_func_getline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define getline to an innocuous variant, in case declares getline. + For example, HP-UX 11i declares gettimeofday. */ +#define getline innocuous_getline + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char getline (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef getline + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char getline (); +/* 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_getline) || defined (__stub___getline) +choke me +#else +char (*f) () = getline; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != getline; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_getline=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_getline=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_getline" >&5 +echo "${ECHO_T}$ac_cv_func_getline" >&6 +if test $ac_cv_func_getline = yes; then + am_getline_needs_run_time_check=yes +else + am_cv_func_working_getline=no +fi + + if test $am_getline_needs_run_time_check = yes; then + echo "$as_me:$LINENO: checking for working getline function" >&5 +echo $ECHO_N "checking for working getline function... $ECHO_C" >&6 +if test "${am_cv_func_working_getline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + echo fooN |tr -d '\012'|tr N '\012' > conftest.data + if test "$cross_compiling" = yes; then + am_cv_func_working_getline=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# include +# include +# include + int main () + { /* Based on a test program from Karl Heuer. */ + char *line = NULL; + size_t siz = 0; + int len; + FILE *in = fopen ("./conftest.data", "r"); + if (!in) + return 1; + len = getline (&line, &siz, in); + exit ((len == 4 && line && strcmp (line, "foo\n") == 0) ? 0 : 1); + } + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + am_cv_func_working_getline=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +am_cv_func_working_getline=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $am_cv_func_working_getline" >&5 +echo "${ECHO_T}$am_cv_func_working_getline" >&6 + fi + + if test $am_cv_func_working_getline = no; then + +cat >>confdefs.h <<\_ACEOF +#define getline gnu_getline +_ACEOF + + case $LIBOBJS in + "getline.$ac_objext" | \ + *" getline.$ac_objext" | \ + "getline.$ac_objext "* | \ + *" getline.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getline.$ac_objext" ;; +esac + + + # Avoid multiple inclusions of getndelim2.o into LIBOBJS. + # This hack won't be needed after gnulib requires Autoconf 2.58 or later. + case " $LIBOBJS " in + *" getndelim2.$ac_objext "* ) ;; + *) case $LIBOBJS in + "getndelim2.$ac_objext" | \ + *" getndelim2.$ac_objext" | \ + "getndelim2.$ac_objext "* | \ + *" getndelim2.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getndelim2.$ac_objext" ;; +esac +;; + esac + + + +for ac_func in getdelim +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + + + + fi + + + MKINSTALLDIRS= + if test -n "$ac_aux_dir"; then + case "$ac_aux_dir" in + /*) MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" ;; + *) MKINSTALLDIRS="\$(top_builddir)/$ac_aux_dir/mkinstalldirs" ;; + esac + fi + if test -z "$MKINSTALLDIRS"; then + MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" + fi + + + + echo "$as_me:$LINENO: checking whether NLS is requested" >&5 +echo $ECHO_N "checking whether NLS is requested... $ECHO_C" >&6 + # Check whether --enable-nls or --disable-nls was given. +if test "${enable_nls+set}" = set; then + enableval="$enable_nls" + USE_NLS=$enableval +else + USE_NLS=yes +fi; + echo "$as_me:$LINENO: result: $USE_NLS" >&5 +echo "${ECHO_T}$USE_NLS" >&6 + + + + + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "msgfmt", so it can be a program name with args. +set dummy msgfmt; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_MSGFMT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case "$MSGFMT" in + [\\/]* | ?:[\\/]*) + ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + if $ac_dir/$ac_word --statistics /dev/null >/dev/null 2>&1 && + (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + ac_cv_path_MSGFMT="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT=":" + ;; +esac +fi +MSGFMT="$ac_cv_path_MSGFMT" +if test "$MSGFMT" != ":"; then + echo "$as_me:$LINENO: result: $MSGFMT" >&5 +echo "${ECHO_T}$MSGFMT" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + # Extract the first word of "gmsgfmt", so it can be a program name with args. +set dummy gmsgfmt; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_GMSGFMT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $GMSGFMT in + [\\/]* | ?:[\\/]*) + ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_GMSGFMT="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT" + ;; +esac +fi +GMSGFMT=$ac_cv_path_GMSGFMT + +if test -n "$GMSGFMT"; then + echo "$as_me:$LINENO: result: $GMSGFMT" >&5 +echo "${ECHO_T}$GMSGFMT" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "xgettext", so it can be a program name with args. +set dummy xgettext; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_XGETTEXT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case "$XGETTEXT" in + [\\/]* | ?:[\\/]*) + ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >/dev/null 2>&1 && + (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + ac_cv_path_XGETTEXT="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":" + ;; +esac +fi +XGETTEXT="$ac_cv_path_XGETTEXT" +if test "$XGETTEXT" != ":"; then + echo "$as_me:$LINENO: result: $XGETTEXT" >&5 +echo "${ECHO_T}$XGETTEXT" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + rm -f messages.po + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "msgmerge", so it can be a program name with args. +set dummy msgmerge; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_MSGMERGE+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case "$MSGMERGE" in + [\\/]* | ?:[\\/]*) + ac_cv_path_MSGMERGE="$MSGMERGE" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + if $ac_dir/$ac_word --update -q /dev/null /dev/null >/dev/null 2>&1; then + ac_cv_path_MSGMERGE="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_MSGMERGE" && ac_cv_path_MSGMERGE=":" + ;; +esac +fi +MSGMERGE="$ac_cv_path_MSGMERGE" +if test "$MSGMERGE" != ":"; then + echo "$as_me:$LINENO: result: $MSGMERGE" >&5 +echo "${ECHO_T}$MSGMERGE" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + if test "$GMSGFMT" != ":"; then + if $GMSGFMT --statistics /dev/null >/dev/null 2>&1 && + (if $GMSGFMT --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + : ; + else + GMSGFMT=`echo "$GMSGFMT" | sed -e 's,^.*/,,'` + echo "$as_me:$LINENO: result: found $GMSGFMT program is not GNU msgfmt; ignore it" >&5 +echo "${ECHO_T}found $GMSGFMT program is not GNU msgfmt; ignore it" >&6 + GMSGFMT=":" + fi + fi + + if test "$XGETTEXT" != ":"; then + if $XGETTEXT --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >/dev/null 2>&1 && + (if $XGETTEXT --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + : ; + else + echo "$as_me:$LINENO: result: found xgettext program is not GNU xgettext; ignore it" >&5 +echo "${ECHO_T}found xgettext program is not GNU xgettext; ignore it" >&6 + XGETTEXT=":" + fi + rm -f messages.po + fi + + ac_config_commands="$ac_config_commands default-1" + + + + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + + +# Check whether --with-gnu-ld or --without-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then + withval="$with_gnu_ld" + test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi; +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + echo "$as_me:$LINENO: checking for ld used by GCC" >&5 +echo $ECHO_N "checking for ld used by GCC... $ECHO_C" >&6 + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | [A-Za-z]:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + echo "$as_me:$LINENO: checking for GNU ld" >&5 +echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6 +else + echo "$as_me:$LINENO: checking for non-GNU ld" >&5 +echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6 +fi +if test "${acl_cv_path_LD+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + if "$acl_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then + test "$with_gnu_ld" != no && break + else + test "$with_gnu_ld" != yes && break + fi + fi + done + IFS="$ac_save_ifs" +else + acl_cv_path_LD="$LD" # Let the user override the test with a path. +fi +fi + +LD="$acl_cv_path_LD" +if test -n "$LD"; then + echo "$as_me:$LINENO: result: $LD" >&5 +echo "${ECHO_T}$LD" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5 +echo "$as_me: error: no acceptable ld found in \$PATH" >&2;} + { (exit 1); exit 1; }; } +echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5 +echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6 +if test "${acl_cv_prog_gnu_ld+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # I'd rather use --version here, but apparently some GNU ld's only accept -v. +if $LD -v 2>&1 &5; then + acl_cv_prog_gnu_ld=yes +else + acl_cv_prog_gnu_ld=no +fi +fi +echo "$as_me:$LINENO: result: $acl_cv_prog_gnu_ld" >&5 +echo "${ECHO_T}$acl_cv_prog_gnu_ld" >&6 +with_gnu_ld=$acl_cv_prog_gnu_ld + + + + echo "$as_me:$LINENO: checking for shared library run path origin" >&5 +echo $ECHO_N "checking for shared library run path origin... $ECHO_C" >&6 +if test "${acl_cv_rpath+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + +fi +echo "$as_me:$LINENO: result: $acl_cv_rpath" >&5 +echo "${ECHO_T}$acl_cv_rpath" >&6 + wl="$acl_cv_wl" + libext="$acl_cv_libext" + shlibext="$acl_cv_shlibext" + hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + hardcode_direct="$acl_cv_hardcode_direct" + hardcode_minus_L="$acl_cv_hardcode_minus_L" + sys_lib_search_path_spec="$acl_cv_sys_lib_search_path_spec" + sys_lib_dlsearch_path_spec="$acl_cv_sys_lib_dlsearch_path_spec" + # Check whether --enable-rpath or --disable-rpath was given. +if test "${enable_rpath+set}" = set; then + enableval="$enable_rpath" + : +else + enable_rpath=yes +fi; + + + + + + + + use_additional=yes + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + +# Check whether --with-libiconv-prefix or --without-libiconv-prefix was given. +if test "${with_libiconv_prefix+set}" = set; then + withval="$with_libiconv_prefix" + + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + else + additional_includedir="$withval/include" + additional_libdir="$withval/lib" + fi + fi + +fi; + LIBICONV= + LTLIBICONV= + INCICONV= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='iconv ' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value" + else + : + fi + else + found_dir= + found_la= + found_so= + found_a= + if test $use_additional = yes; then + if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then + found_dir="$additional_libdir" + found_so="$additional_libdir/lib$name.$shlibext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + else + if test -f "$additional_libdir/lib$name.$libext"; then + found_dir="$additional_libdir" + found_a="$additional_libdir/lib$name.$libext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIBICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then + found_dir="$dir" + found_so="$dir/lib$name.$shlibext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + else + if test -f "$dir/lib$name.$libext"; then + found_dir="$dir" + found_a="$dir/lib$name.$libext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" + else + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + if test "$hardcode_direct" = yes; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" + else + if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + haveit= + for x in $LDFLAGS $LIBICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir" + fi + if test "$hardcode_minus_L" != no; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" + else + LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a" + else + LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name" + fi + fi + additional_includedir= + case "$found_dir" in + */lib | */lib/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INCICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + if test -n "$found_la"; then + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + if test "X$additional_libdir" != "X/usr/lib"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/lib"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIBICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIBICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + LIBICONV="${LIBICONV}${LIBICONV:+ }$dep" + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep" + ;; + esac + done + fi + else + LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$hardcode_libdir_separator"; then + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" + else + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + for found_dir in $ltrpathdirs; do + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir" + done + fi + + + + + + + + + + + + + + + + + + + echo "$as_me:$LINENO: checking whether NLS is requested" >&5 +echo $ECHO_N "checking whether NLS is requested... $ECHO_C" >&6 + # Check whether --enable-nls or --disable-nls was given. +if test "${enable_nls+set}" = set; then + enableval="$enable_nls" + USE_NLS=$enableval +else + USE_NLS=yes +fi; + echo "$as_me:$LINENO: result: $USE_NLS" >&5 +echo "${ECHO_T}$USE_NLS" >&6 + + + + + LIBINTL= + LTLIBINTL= + POSUB= + + if test "$USE_NLS" = "yes"; then + gt_use_preinstalled_gnugettext=no + + + + + + + echo "$as_me:$LINENO: checking for GNU gettext in libc" >&5 +echo $ECHO_N "checking for GNU gettext in libc... $ECHO_C" >&6 +if test "${gt_cv_func_gnugettext1_libc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +extern int _nl_msg_cat_cntr; +extern int *_nl_domain_bindings; +int +main () +{ +bindtextdomain ("", ""); +return (int) gettext ("") + _nl_msg_cat_cntr + *_nl_domain_bindings + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gt_cv_func_gnugettext1_libc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gt_cv_func_gnugettext1_libc=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $gt_cv_func_gnugettext1_libc" >&5 +echo "${ECHO_T}$gt_cv_func_gnugettext1_libc" >&6 + + if test "$gt_cv_func_gnugettext1_libc" != "yes"; then + + + + + + am_save_CPPFLAGS="$CPPFLAGS" + + for element in $INCICONV; do + haveit= + for x in $CPPFLAGS; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" + fi + done + + + echo "$as_me:$LINENO: checking for iconv" >&5 +echo $ECHO_N "checking for iconv... $ECHO_C" >&6 +if test "${am_cv_func_iconv+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + am_cv_func_iconv="no, consider installing GNU libiconv" + am_cv_lib_iconv=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +int +main () +{ +iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + am_cv_func_iconv=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test "$am_cv_func_iconv" != yes; then + am_save_LIBS="$LIBS" + LIBS="$LIBS $LIBICONV" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +int +main () +{ +iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + am_cv_lib_iconv=yes + am_cv_func_iconv=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$am_save_LIBS" + fi + +fi +echo "$as_me:$LINENO: result: $am_cv_func_iconv" >&5 +echo "${ECHO_T}$am_cv_func_iconv" >&6 + if test "$am_cv_func_iconv" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ICONV 1 +_ACEOF + + fi + if test "$am_cv_lib_iconv" = yes; then + echo "$as_me:$LINENO: checking how to link with libiconv" >&5 +echo $ECHO_N "checking how to link with libiconv... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: $LIBICONV" >&5 +echo "${ECHO_T}$LIBICONV" >&6 + else + CPPFLAGS="$am_save_CPPFLAGS" + LIBICONV= + LTLIBICONV= + fi + + + + + + + use_additional=yes + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + +# Check whether --with-libintl-prefix or --without-libintl-prefix was given. +if test "${with_libintl_prefix+set}" = set; then + withval="$with_libintl_prefix" + + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + else + additional_includedir="$withval/include" + additional_libdir="$withval/lib" + fi + fi + +fi; + LIBINTL= + LTLIBINTL= + INCINTL= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='intl ' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIBINTL="${LIBINTL}${LIBINTL:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$value" + else + : + fi + else + found_dir= + found_la= + found_so= + found_a= + if test $use_additional = yes; then + if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then + found_dir="$additional_libdir" + found_so="$additional_libdir/lib$name.$shlibext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + else + if test -f "$additional_libdir/lib$name.$libext"; then + found_dir="$additional_libdir" + found_a="$additional_libdir/lib$name.$libext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIBINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then + found_dir="$dir" + found_so="$dir/lib$name.$shlibext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + else + if test -f "$dir/lib$name.$libext"; then + found_dir="$dir" + found_a="$dir/lib$name.$libext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so" + else + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + if test "$hardcode_direct" = yes; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so" + else + if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so" + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + haveit= + for x in $LDFLAGS $LIBINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir" + fi + if test "$hardcode_minus_L" != no; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so" + else + LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_a" + else + LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir -l$name" + fi + fi + additional_includedir= + case "$found_dir" in + */lib | */lib/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INCINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + INCINTL="${INCINTL}${INCINTL:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + if test -n "$found_la"; then + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + if test "X$additional_libdir" != "X/usr/lib"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/lib"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIBINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LIBINTL="${LIBINTL}${LIBINTL:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIBINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + LIBINTL="${LIBINTL}${LIBINTL:+ }$dep" + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$dep" + ;; + esac + done + fi + else + LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name" + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$hardcode_libdir_separator"; then + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBINTL="${LIBINTL}${LIBINTL:+ }$flag" + else + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBINTL="${LIBINTL}${LIBINTL:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + for found_dir in $ltrpathdirs; do + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-R$found_dir" + done + fi + + echo "$as_me:$LINENO: checking for GNU gettext in libintl" >&5 +echo $ECHO_N "checking for GNU gettext in libintl... $ECHO_C" >&6 +if test "${gt_cv_func_gnugettext1_libintl+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + gt_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $INCINTL" + gt_save_LIBS="$LIBS" + LIBS="$LIBS $LIBINTL" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +extern int _nl_msg_cat_cntr; +extern +#ifdef __cplusplus +"C" +#endif +const char *_nl_expand_alias (); +int +main () +{ +bindtextdomain ("", ""); +return (int) gettext ("") + _nl_msg_cat_cntr + *_nl_expand_alias (0) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gt_cv_func_gnugettext1_libintl=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gt_cv_func_gnugettext1_libintl=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test "$gt_cv_func_gnugettext1_libintl" != yes && test -n "$LIBICONV"; then + LIBS="$LIBS $LIBICONV" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +extern int _nl_msg_cat_cntr; +extern +#ifdef __cplusplus +"C" +#endif +const char *_nl_expand_alias (); +int +main () +{ +bindtextdomain ("", ""); +return (int) gettext ("") + _nl_msg_cat_cntr + *_nl_expand_alias (0) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + LIBINTL="$LIBINTL $LIBICONV" + LTLIBINTL="$LTLIBINTL $LTLIBICONV" + gt_cv_func_gnugettext1_libintl=yes + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + fi + CPPFLAGS="$gt_save_CPPFLAGS" + LIBS="$gt_save_LIBS" +fi +echo "$as_me:$LINENO: result: $gt_cv_func_gnugettext1_libintl" >&5 +echo "${ECHO_T}$gt_cv_func_gnugettext1_libintl" >&6 + fi + + if test "$gt_cv_func_gnugettext1_libc" = "yes" \ + || { test "$gt_cv_func_gnugettext1_libintl" = "yes" \ + && test "$PACKAGE" != gettext-runtime \ + && test "$PACKAGE" != gettext-tools; }; then + gt_use_preinstalled_gnugettext=yes + else + LIBINTL= + LTLIBINTL= + INCINTL= + fi + + + + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + +cat >>confdefs.h <<\_ACEOF +#define ENABLE_NLS 1 +_ACEOF + + else + USE_NLS=no + fi + fi + + echo "$as_me:$LINENO: checking whether to use NLS" >&5 +echo $ECHO_N "checking whether to use NLS... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: $USE_NLS" >&5 +echo "${ECHO_T}$USE_NLS" >&6 + if test "$USE_NLS" = "yes"; then + echo "$as_me:$LINENO: checking where the gettext function comes from" >&5 +echo $ECHO_N "checking where the gettext function comes from... $ECHO_C" >&6 + if test "$gt_use_preinstalled_gnugettext" = "yes"; then + if test "$gt_cv_func_gnugettext1_libintl" = "yes"; then + gt_source="external libintl" + else + gt_source="libc" + fi + else + gt_source="included intl directory" + fi + echo "$as_me:$LINENO: result: $gt_source" >&5 +echo "${ECHO_T}$gt_source" >&6 + fi + + if test "$USE_NLS" = "yes"; then + + if test "$gt_use_preinstalled_gnugettext" = "yes"; then + if test "$gt_cv_func_gnugettext1_libintl" = "yes"; then + echo "$as_me:$LINENO: checking how to link with libintl" >&5 +echo $ECHO_N "checking how to link with libintl... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: $LIBINTL" >&5 +echo "${ECHO_T}$LIBINTL" >&6 + + for element in $INCINTL; do + haveit= + for x in $CPPFLAGS; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" + fi + done + + fi + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETTEXT 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DCGETTEXT 1 +_ACEOF + + fi + + POSUB=po + fi + + + + INTLLIBS="$LIBINTL" + + + + + + + echo "$as_me:$LINENO: checking for stdbool.h that conforms to C99" >&5 +echo $ECHO_N "checking for stdbool.h that conforms to C99... $ECHO_C" >&6 +if test "${ac_cv_header_stdbool_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #include + #ifndef bool + "error: bool is not defined" + #endif + #ifndef false + "error: false is not defined" + #endif + #if false + "error: false is not 0" + #endif + #ifndef true + "error: false is not defined" + #endif + #if true != 1 + "error: true is not 1" + #endif + #ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" + #endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) -0.5 == true ? 1 : -1]; + bool e = &s; + char f[(_Bool) -0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + +int +main () +{ + return !a + !b + !c + !d + !e + !f + !g + !h + !i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdbool_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdbool_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdbool_h" >&5 +echo "${ECHO_T}$ac_cv_header_stdbool_h" >&6 + echo "$as_me:$LINENO: checking for _Bool" >&5 +echo $ECHO_N "checking for _Bool... $ECHO_C" >&6 +if test "${ac_cv_type__Bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((_Bool *) 0) + return 0; +if (sizeof (_Bool)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type__Bool=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type__Bool=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type__Bool" >&5 +echo "${ECHO_T}$ac_cv_type__Bool" >&6 +if test $ac_cv_type__Bool = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + + +fi + + if test $ac_cv_header_stdbool_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STDBOOL_H 1 +_ACEOF + + fi + + + + # Define two additional variables used in the Makefile substitution. + + if test "$ac_cv_header_stdbool_h" = yes; then + STDBOOL_H='' + else + STDBOOL_H='stdbool.h' + fi + + + if test "$ac_cv_type__Bool" = yes; then + HAVE__BOOL=1 + else + HAVE__BOOL=0 + fi + + + + + + + +for ac_header in $gl_header_list +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + + + + + + + + + + + + + echo "$as_me:$LINENO: checking whether getenv is declared" >&5 +echo $ECHO_N "checking whether getenv is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_getenv+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef getenv + char *p = (char *) getenv; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_getenv=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_getenv=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_getenv" >&5 +echo "${ECHO_T}$ac_cv_have_decl_getenv" >&6 +if test $ac_cv_have_decl_getenv = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETENV 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETENV 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking for inttypes.h" >&5 +echo $ECHO_N "checking for inttypes.h... $ECHO_C" >&6 +if test "${jm_ac_cv_header_inttypes_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +int +main () +{ +uintmax_t i = (uintmax_t) -1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + jm_ac_cv_header_inttypes_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +jm_ac_cv_header_inttypes_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $jm_ac_cv_header_inttypes_h" >&5 +echo "${ECHO_T}$jm_ac_cv_header_inttypes_h" >&6 + if test $jm_ac_cv_header_inttypes_h = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_INTTYPES_H_WITH_UINTMAX 1 +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking for stdint.h" >&5 +echo $ECHO_N "checking for stdint.h... $ECHO_C" >&6 +if test "${jm_ac_cv_header_stdint_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +int +main () +{ +uintmax_t i = (uintmax_t) -1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + jm_ac_cv_header_stdint_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +jm_ac_cv_header_stdint_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $jm_ac_cv_header_stdint_h" >&5 +echo "${ECHO_T}$jm_ac_cv_header_stdint_h" >&6 + if test $jm_ac_cv_header_stdint_h = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STDINT_H_WITH_UINTMAX 1 +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking for unsigned long long" >&5 +echo $ECHO_N "checking for unsigned long long... $ECHO_C" >&6 +if test "${ac_cv_type_unsigned_long_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +unsigned long long ull = 1ULL; int i = 63; +int +main () +{ +unsigned long long ullmax = (unsigned long long) -1; + return ull << i | ull >> i | ullmax / ull | ullmax % ull; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_unsigned_long_long=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_unsigned_long_long=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_unsigned_long_long" >&5 +echo "${ECHO_T}$ac_cv_type_unsigned_long_long" >&6 + if test $ac_cv_type_unsigned_long_long = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_UNSIGNED_LONG_LONG 1 +_ACEOF + + fi + + + + + if test $jm_ac_cv_header_inttypes_h = no && test $jm_ac_cv_header_stdint_h = no; then + + test $ac_cv_type_unsigned_long_long = yes \ + && ac_type='unsigned long long' \ + || ac_type='unsigned long' + +cat >>confdefs.h <<_ACEOF +#define uintmax_t $ac_type +_ACEOF + + else + +cat >>confdefs.h <<\_ACEOF +#define HAVE_UINTMAX_T 1 +_ACEOF + + fi + + +for ac_func in mkstemp +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + if test $ac_cv_func_mkstemp = no; then + utils_cv_func_mkstemp_limitations=yes + else + echo "$as_me:$LINENO: checking for mkstemp limitations" >&5 +echo $ECHO_N "checking for mkstemp limitations... $ECHO_C" >&6 +if test "${utils_cv_func_mkstemp_limitations+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + if test "$cross_compiling" = yes; then + utils_cv_func_mkstemp_limitations=yes + +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# include + int main () + { + int i; + for (i = 0; i < 30; i++) + { + char template[] = "conftestXXXXXX"; + int fd = mkstemp (template); + if (fd == -1) + exit (1); + close (fd); + } + exit (0); + } + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + utils_cv_func_mkstemp_limitations=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +utils_cv_func_mkstemp_limitations=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +echo "$as_me:$LINENO: result: $utils_cv_func_mkstemp_limitations" >&5 +echo "${ECHO_T}$utils_cv_func_mkstemp_limitations" >&6 + fi + + if test $utils_cv_func_mkstemp_limitations = yes; then + case $LIBOBJS in + "mkstemp.$ac_objext" | \ + *" mkstemp.$ac_objext" | \ + "mkstemp.$ac_objext "* | \ + *" mkstemp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS mkstemp.$ac_objext" ;; +esac + + case $LIBOBJS in + "tempname.$ac_objext" | \ + *" tempname.$ac_objext" | \ + "tempname.$ac_objext "* | \ + *" tempname.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS tempname.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define mkstemp rpl_mkstemp +_ACEOF + + + + + + + + : + + + + + + + + + + + + +for ac_header in stdint.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in __secure_getenv gettimeofday +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + : + + + + + + + + fi + +echo "$as_me:$LINENO: checking for C/C++ restrict keyword" >&5 +echo $ECHO_N "checking for C/C++ restrict keyword... $ECHO_C" >&6 +if test "${gl_cv_c_restrict+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + gl_cv_c_restrict=no + # Try the official restrict keyword, then gcc's __restrict, and + # the less common variants. + for ac_kw in restrict __restrict __restrict__ _Restrict; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +float * $ac_kw x; +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gl_cv_c_restrict=$ac_kw; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + +fi +echo "$as_me:$LINENO: result: $gl_cv_c_restrict" >&5 +echo "${ECHO_T}$gl_cv_c_restrict" >&6 + case $gl_cv_c_restrict in + restrict) ;; + no) +cat >>confdefs.h <<\_ACEOF +#define restrict +_ACEOF + ;; + *) cat >>confdefs.h <<_ACEOF +#define restrict $gl_cv_c_restrict +_ACEOF + ;; + esac + + + echo "$as_me:$LINENO: checking whether system is Windows or MSDOS" >&5 +echo $ECHO_N "checking whether system is Windows or MSDOS... $ECHO_C" >&6 +if test "${ac_cv_win_or_dos+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#if !defined _WIN32 && !defined __WIN32__ && !defined __MSDOS__ && !defined __CYGWIN__ +neither MSDOS nor Windows +#endif + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_win_or_dos=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_win_or_dos=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $ac_cv_win_or_dos" >&5 +echo "${ECHO_T}$ac_cv_win_or_dos" >&6 + + if test x"$ac_cv_win_or_dos" = xyes; then + ac_fs_accepts_drive_letter_prefix=1 + ac_fs_backslash_is_file_name_separator=1 + else + ac_fs_accepts_drive_letter_prefix=0 + ac_fs_backslash_is_file_name_separator=0 + fi + + + + + +cat >>confdefs.h <<_ACEOF +#define FILESYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX $ac_fs_accepts_drive_letter_prefix +_ACEOF + + + + + + +cat >>confdefs.h <<_ACEOF +#define FILESYSTEM_BACKSLASH_IS_FILE_NAME_SEPARATOR $ac_fs_backslash_is_file_name_separator +_ACEOF + + + + + + + + : + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +echo "$as_me:$LINENO: checking for working alloca.h" >&5 +echo $ECHO_N "checking for working alloca.h... $ECHO_C" >&6 +if test "${ac_cv_working_alloca_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +char *p = (char *) alloca (2 * sizeof (int)); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_working_alloca_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_working_alloca_h=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5 +echo "${ECHO_T}$ac_cv_working_alloca_h" >&6 +if test $ac_cv_working_alloca_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA_H 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for alloca" >&5 +echo $ECHO_N "checking for alloca... $ECHO_C" >&6 +if test "${ac_cv_func_alloca_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# ifdef _MSC_VER +# include +# define alloca _alloca +# else +# if HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +int +main () +{ +char *p = (char *) alloca (1); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_alloca_works=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_alloca_works=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5 +echo "${ECHO_T}$ac_cv_func_alloca_works" >&6 + +if test $ac_cv_func_alloca_works = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA 1 +_ACEOF + +else + # The SVR3 libPW and SVR4 libucb both contain incompatible functions +# that cause trouble. Some versions do not even contain alloca or +# contain a buggy version. If you still want to use their alloca, +# use ar to extract alloca.o from them instead of compiling alloca.c. + +ALLOCA=alloca.$ac_objext + +cat >>confdefs.h <<\_ACEOF +#define C_ALLOCA 1 +_ACEOF + + +echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5 +echo $ECHO_N "checking whether \`alloca.c' needs Cray hooks... $ECHO_C" >&6 +if test "${ac_cv_os_cray+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if defined(CRAY) && ! defined(CRAY2) +webecray +#else +wenotbecray +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "webecray" >/dev/null 2>&1; then + ac_cv_os_cray=yes +else + ac_cv_os_cray=no +fi +rm -f conftest* + +fi +echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5 +echo "${ECHO_T}$ac_cv_os_cray" >&6 +if test $ac_cv_os_cray = yes; then + for ac_func in _getb67 GETB67 getb67; do + as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + +cat >>confdefs.h <<_ACEOF +#define CRAY_STACKSEG_END $ac_func +_ACEOF + + break +fi + + done +fi + +echo "$as_me:$LINENO: checking stack direction for C alloca" >&5 +echo $ECHO_N "checking stack direction for C alloca... $ECHO_C" >&6 +if test "${ac_cv_c_stack_direction+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_c_stack_direction=0 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +find_stack_direction () +{ + static char *addr = 0; + auto char dummy; + if (addr == 0) + { + addr = &dummy; + return find_stack_direction (); + } + else + return (&dummy > addr) ? 1 : -1; +} + +int +main () +{ + exit (find_stack_direction () < 0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_stack_direction=1 +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_stack_direction=-1 +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5 +echo "${ECHO_T}$ac_cv_c_stack_direction" >&6 + +cat >>confdefs.h <<_ACEOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +_ACEOF + + +fi + + + + + + + if test $ac_cv_func_alloca_works = no; then + : + fi + + # Define an additional variable used in the Makefile substitution. + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#if defined __GNUC__ || defined _MSC_VER || !HAVE_ALLOCA_H + Need own alloca +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "Need own alloca" >/dev/null 2>&1; then + ALLOCA_H=alloca.h +else + ALLOCA_H= +fi +rm -f conftest* + + + + + +for ac_func in atexit +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + if test $ac_cv_func_atexit = no; then + + : + + fi + +echo "$as_me:$LINENO: checking for mbstate_t" >&5 +echo $ECHO_N "checking for mbstate_t... $ECHO_C" >&6 +if test "${ac_cv_type_mbstate_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +# include +int +main () +{ +mbstate_t x; return sizeof x; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_mbstate_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_mbstate_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_mbstate_t" >&5 +echo "${ECHO_T}$ac_cv_type_mbstate_t" >&6 + if test $ac_cv_type_mbstate_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MBSTATE_T 1 +_ACEOF + + else + +cat >>confdefs.h <<\_ACEOF +#define mbstate_t int +_ACEOF + + fi + + FNMATCH_H= + echo "$as_me:$LINENO: checking for working POSIX fnmatch" >&5 +echo $ECHO_N "checking for working POSIX fnmatch... $ECHO_C" >&6 +if test "${ac_cv_func_fnmatch_posix+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Some versions of Solaris, SCO, and the GNU C Library + # have a broken or incompatible fnmatch. + # So we run a test program. If we are cross-compiling, take no chance. + # Thanks to John Oleynick, Franc,ois Pinard, and Paul Eggert for this test. + if test "$cross_compiling" = yes; then + ac_cv_func_fnmatch_posix=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# include +# include +# define y(a, b, c) (fnmatch (a, b, c) == 0) +# define n(a, b, c) (fnmatch (a, b, c) == FNM_NOMATCH) + +int +main () +{ +exit + (!(y ("a*", "abc", 0) + && n ("d*/*1", "d/s/1", FNM_PATHNAME) + && y ("a\\\\bc", "abc", 0) + && n ("a\\\\bc", "abc", FNM_NOESCAPE) + && y ("*x", ".x", 0) + && n ("*x", ".x", FNM_PERIOD) + && 1)); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fnmatch_posix=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fnmatch_posix=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fnmatch_posix" >&5 +echo "${ECHO_T}$ac_cv_func_fnmatch_posix" >&6 +if test $ac_cv_func_fnmatch_posix = yes; then + rm -f lib/fnmatch.h +else + echo "$as_me:$LINENO: checking whether getenv is declared" >&5 +echo $ECHO_N "checking whether getenv is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_getenv+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef getenv + char *p = (char *) getenv; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_getenv=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_getenv=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_getenv" >&5 +echo "${ECHO_T}$ac_cv_have_decl_getenv" >&6 +if test $ac_cv_have_decl_getenv = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETENV 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETENV 0 +_ACEOF + + +fi + + + + + + +for ac_func in btowc mbsrtowcs mempcpy wmempcpy +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +for ac_header in wchar.h wctype.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +case $LIBOBJS in + "fnmatch.$ac_objext" | \ + *" fnmatch.$ac_objext" | \ + "fnmatch.$ac_objext "* | \ + *" fnmatch.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS fnmatch.$ac_objext" ;; +esac + +FNMATCH_H=fnmatch.h + +fi + + + if test $ac_cv_func_fnmatch_posix != yes; then + +cat >>confdefs.h <<\_ACEOF +#define fnmatch posix_fnmatch +_ACEOF + + fi + + + + +for ac_func in gethostname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + if test $ac_cv_func_gethostname = no; then + + +for ac_func in uname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + fi + + + echo "$as_me:$LINENO: checking whether clearerr_unlocked is declared" >&5 +echo $ECHO_N "checking whether clearerr_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_clearerr_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef clearerr_unlocked + char *p = (char *) clearerr_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_clearerr_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_clearerr_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_clearerr_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_clearerr_unlocked" >&6 +if test $ac_cv_have_decl_clearerr_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_CLEARERR_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_CLEARERR_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether feof_unlocked is declared" >&5 +echo $ECHO_N "checking whether feof_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_feof_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef feof_unlocked + char *p = (char *) feof_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_feof_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_feof_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_feof_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_feof_unlocked" >&6 +if test $ac_cv_have_decl_feof_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FEOF_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FEOF_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether ferror_unlocked is declared" >&5 +echo $ECHO_N "checking whether ferror_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_ferror_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef ferror_unlocked + char *p = (char *) ferror_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_ferror_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_ferror_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_ferror_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_ferror_unlocked" >&6 +if test $ac_cv_have_decl_ferror_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FERROR_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FERROR_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether fflush_unlocked is declared" >&5 +echo $ECHO_N "checking whether fflush_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_fflush_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef fflush_unlocked + char *p = (char *) fflush_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_fflush_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_fflush_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_fflush_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_fflush_unlocked" >&6 +if test $ac_cv_have_decl_fflush_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FFLUSH_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FFLUSH_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether fgets_unlocked is declared" >&5 +echo $ECHO_N "checking whether fgets_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_fgets_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef fgets_unlocked + char *p = (char *) fgets_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_fgets_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_fgets_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_fgets_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_fgets_unlocked" >&6 +if test $ac_cv_have_decl_fgets_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FGETS_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FGETS_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether fputc_unlocked is declared" >&5 +echo $ECHO_N "checking whether fputc_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_fputc_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef fputc_unlocked + char *p = (char *) fputc_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_fputc_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_fputc_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_fputc_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_fputc_unlocked" >&6 +if test $ac_cv_have_decl_fputc_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FPUTC_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FPUTC_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether fputs_unlocked is declared" >&5 +echo $ECHO_N "checking whether fputs_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_fputs_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef fputs_unlocked + char *p = (char *) fputs_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_fputs_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_fputs_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_fputs_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_fputs_unlocked" >&6 +if test $ac_cv_have_decl_fputs_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FPUTS_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FPUTS_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether fread_unlocked is declared" >&5 +echo $ECHO_N "checking whether fread_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_fread_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef fread_unlocked + char *p = (char *) fread_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_fread_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_fread_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_fread_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_fread_unlocked" >&6 +if test $ac_cv_have_decl_fread_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FREAD_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FREAD_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether fwrite_unlocked is declared" >&5 +echo $ECHO_N "checking whether fwrite_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_fwrite_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef fwrite_unlocked + char *p = (char *) fwrite_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_fwrite_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_fwrite_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_fwrite_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_fwrite_unlocked" >&6 +if test $ac_cv_have_decl_fwrite_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FWRITE_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FWRITE_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether getc_unlocked is declared" >&5 +echo $ECHO_N "checking whether getc_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_getc_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef getc_unlocked + char *p = (char *) getc_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_getc_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_getc_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_getc_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_getc_unlocked" >&6 +if test $ac_cv_have_decl_getc_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETC_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETC_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether getchar_unlocked is declared" >&5 +echo $ECHO_N "checking whether getchar_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_getchar_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef getchar_unlocked + char *p = (char *) getchar_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_getchar_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_getchar_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_getchar_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_getchar_unlocked" >&6 +if test $ac_cv_have_decl_getchar_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETCHAR_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETCHAR_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether putc_unlocked is declared" >&5 +echo $ECHO_N "checking whether putc_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_putc_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef putc_unlocked + char *p = (char *) putc_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_putc_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_putc_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_putc_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_putc_unlocked" >&6 +if test $ac_cv_have_decl_putc_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_PUTC_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_PUTC_UNLOCKED 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether putchar_unlocked is declared" >&5 +echo $ECHO_N "checking whether putchar_unlocked is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_putchar_unlocked+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef putchar_unlocked + char *p = (char *) putchar_unlocked; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_putchar_unlocked=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_putchar_unlocked=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_putchar_unlocked" >&5 +echo "${ECHO_T}$ac_cv_have_decl_putchar_unlocked" >&6 +if test $ac_cv_have_decl_putchar_unlocked = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_PUTCHAR_UNLOCKED 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_PUTCHAR_UNLOCKED 0 +_ACEOF + + +fi + + + + + + + + : + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +for ac_func in $gl_func_list +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + + + + +echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm *tp; tp->tm_sec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_struct_tm=sys/time.h +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + + + echo "$as_me:$LINENO: checking for struct tm.tm_gmtoff" >&5 +echo $ECHO_N "checking for struct tm.tm_gmtoff... $ECHO_C" >&6 +if test "${ac_cv_member_struct_tm_tm_gmtoff+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +int +main () +{ +static struct tm ac_aggr; +if (ac_aggr.tm_gmtoff) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_tm_tm_gmtoff=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +int +main () +{ +static struct tm ac_aggr; +if (sizeof ac_aggr.tm_gmtoff) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_tm_tm_gmtoff=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_tm_tm_gmtoff=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_tm_tm_gmtoff" >&5 +echo "${ECHO_T}$ac_cv_member_struct_tm_tm_gmtoff" >&6 +if test $ac_cv_member_struct_tm_tm_gmtoff = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TM_GMTOFF 1 +_ACEOF + +fi + + + + + echo "$as_me:$LINENO: checking whether tzset clobbers localtime buffer" >&5 +echo $ECHO_N "checking whether tzset clobbers localtime buffer... $ECHO_C" >&6 +if test "${gl_cv_func_tzset_clobber+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + if test "$cross_compiling" = yes; then + gl_cv_func_tzset_clobber=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include + +int +main () +{ + time_t t1 = 853958121; + struct tm *p, s; + putenv ("TZ=GMT0"); + p = localtime (&t1); + s = *p; + putenv ("TZ=EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00"); + tzset (); + exit (p->tm_year != s.tm_year + || p->tm_mon != s.tm_mon + || p->tm_mday != s.tm_mday + || p->tm_hour != s.tm_hour + || p->tm_min != s.tm_min + || p->tm_sec != s.tm_sec); +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gl_cv_func_tzset_clobber=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +gl_cv_func_tzset_clobber=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $gl_cv_func_tzset_clobber" >&5 +echo "${ECHO_T}$gl_cv_func_tzset_clobber" >&6 + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_RUN_TZSET_TEST 1 +_ACEOF + + + if test $gl_cv_func_tzset_clobber = yes; then + + case $LIBOBJS in + "gettimeofday.$ac_objext" | \ + *" gettimeofday.$ac_objext" | \ + "gettimeofday.$ac_objext "* | \ + *" gettimeofday.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS gettimeofday.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define gmtime rpl_gmtime +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define localtime rpl_localtime +_ACEOF + + + + +cat >>confdefs.h <<\_ACEOF +#define tzset rpl_tzset +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define TZSET_CLOBBERS_LOCALTIME_BUFFER 1 +_ACEOF + + fi + + + + +for ac_func in strftime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + # strftime is in -lintl on SCO UNIX. +echo "$as_me:$LINENO: checking for strftime in -lintl" >&5 +echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 +if test "${ac_cv_lib_intl_strftime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime (); +int +main () +{ +strftime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_intl_strftime=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_intl_strftime=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5 +echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 +if test $ac_cv_lib_intl_strftime = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_STRFTIME 1 +_ACEOF + +LIBS="-lintl $LIBS" +fi + +fi +done + + + + : + + + + + + +for ac_func in tzset +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + # This defines (or not) HAVE_TZNAME and HAVE_TM_ZONE. + echo "$as_me:$LINENO: checking for struct tm.tm_zone" >&5 +echo $ECHO_N "checking for struct tm.tm_zone... $ECHO_C" >&6 +if test "${ac_cv_member_struct_tm_tm_zone+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_cv_struct_tm> + + +int +main () +{ +static struct tm ac_aggr; +if (ac_aggr.tm_zone) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_tm_tm_zone=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_cv_struct_tm> + + +int +main () +{ +static struct tm ac_aggr; +if (sizeof ac_aggr.tm_zone) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_tm_tm_zone=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_tm_tm_zone=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_tm_tm_zone" >&5 +echo "${ECHO_T}$ac_cv_member_struct_tm_tm_zone" >&6 +if test $ac_cv_member_struct_tm_tm_zone = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_TM_TM_ZONE 1 +_ACEOF + + +fi + +if test "$ac_cv_member_struct_tm_tm_zone" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TM_ZONE 1 +_ACEOF + +else + echo "$as_me:$LINENO: checking for tzname" >&5 +echo $ECHO_N "checking for tzname... $ECHO_C" >&6 +if test "${ac_cv_var_tzname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifndef tzname /* For SGI. */ +extern char *tzname[]; /* RS6000 and others reject char **tzname. */ +#endif + +int +main () +{ +atoi(*tzname); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_var_tzname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_var_tzname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_var_tzname" >&5 +echo "${ECHO_T}$ac_cv_var_tzname" >&6 + if test $ac_cv_var_tzname = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TZNAME 1 +_ACEOF + + fi +fi + + + + +for ac_func in mblen mbrlen +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + echo "$as_me:$LINENO: checking for mbstate_t" >&5 +echo $ECHO_N "checking for mbstate_t... $ECHO_C" >&6 +if test "${ac_cv_type_mbstate_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +# include +int +main () +{ +mbstate_t x; return sizeof x; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_mbstate_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_mbstate_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_mbstate_t" >&5 +echo "${ECHO_T}$ac_cv_type_mbstate_t" >&6 + if test $ac_cv_type_mbstate_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MBSTATE_T 1 +_ACEOF + + else + +cat >>confdefs.h <<\_ACEOF +#define mbstate_t int +_ACEOF + + fi + + + + + + + : + + + + + + +cat >>confdefs.h <<\_ACEOF +#define my_strftime nstrftime +_ACEOF + + + + +for ac_func in memmove +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + if test $ac_cv_func_memmove = no; then + + : + + fi + + + + +for ac_header in stdlib.h sys/time.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in alarm +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking for working mktime" >&5 +echo $ECHO_N "checking for working mktime... $ECHO_C" >&6 +if test "${ac_cv_func_working_mktime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_working_mktime=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Test program from Paul Eggert and Tony Leneis. */ +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_STDLIB_H +# include +#endif + +#if HAVE_UNISTD_H +# include +#endif + +#if !HAVE_ALARM +# define alarm(X) /* empty */ +#endif + +/* Work around redefinition to rpl_putenv by other config tests. */ +#undef putenv + +static time_t time_t_max; +static time_t time_t_min; + +/* Values we'll use to set the TZ environment variable. */ +static char *tz_strings[] = { + (char *) 0, "TZ=GMT0", "TZ=JST-9", + "TZ=EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00" +}; +#define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0])) + +/* Fail if mktime fails to convert a date in the spring-forward gap. + Based on a problem report from Andreas Jaeger. */ +static void +spring_forward_gap () +{ + /* glibc (up to about 1998-10-07) failed this test. */ + struct tm tm; + + /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0" + instead of "TZ=America/Vancouver" in order to detect the bug even + on systems that don't support the Olson extension, or don't have the + full zoneinfo tables installed. */ + putenv ("TZ=PST8PDT,M4.1.0,M10.5.0"); + + tm.tm_year = 98; + tm.tm_mon = 3; + tm.tm_mday = 5; + tm.tm_hour = 2; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + if (mktime (&tm) == (time_t)-1) + exit (1); +} + +static void +mktime_test1 (now) + time_t now; +{ + struct tm *lt; + if ((lt = localtime (&now)) && mktime (lt) != now) + exit (1); +} + +static void +mktime_test (now) + time_t now; +{ + mktime_test1 (now); + mktime_test1 ((time_t) (time_t_max - now)); + mktime_test1 ((time_t) (time_t_min + now)); +} + +static void +irix_6_4_bug () +{ + /* Based on code from Ariel Faigon. */ + struct tm tm; + tm.tm_year = 96; + tm.tm_mon = 3; + tm.tm_mday = 0; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + mktime (&tm); + if (tm.tm_mon != 2 || tm.tm_mday != 31) + exit (1); +} + +static void +bigtime_test (j) + int j; +{ + struct tm tm; + time_t now; + tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_hour = tm.tm_min = tm.tm_sec = j; + now = mktime (&tm); + if (now != (time_t) -1) + { + struct tm *lt = localtime (&now); + if (! (lt + && lt->tm_year == tm.tm_year + && lt->tm_mon == tm.tm_mon + && lt->tm_mday == tm.tm_mday + && lt->tm_hour == tm.tm_hour + && lt->tm_min == tm.tm_min + && lt->tm_sec == tm.tm_sec + && lt->tm_yday == tm.tm_yday + && lt->tm_wday == tm.tm_wday + && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst) + == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst)))) + exit (1); + } +} + +int +main () +{ + time_t t, delta; + int i, j; + + /* This test makes some buggy mktime implementations loop. + Give up after 60 seconds; a mktime slower than that + isn't worth using anyway. */ + alarm (60); + + for (time_t_max = 1; 0 < time_t_max; time_t_max *= 2) + continue; + time_t_max--; + if ((time_t) -1 < 0) + for (time_t_min = -1; (time_t) (time_t_min * 2) < 0; time_t_min *= 2) + continue; + delta = time_t_max / 997; /* a suitable prime number */ + for (i = 0; i < N_STRINGS; i++) + { + if (tz_strings[i]) + putenv (tz_strings[i]); + + for (t = 0; t <= time_t_max - delta; t += delta) + mktime_test (t); + mktime_test ((time_t) 1); + mktime_test ((time_t) (60 * 60)); + mktime_test ((time_t) (60 * 60 * 24)); + + for (j = 1; 0 < j; j *= 2) + bigtime_test (j); + bigtime_test (j - 1); + } + irix_6_4_bug (); + spring_forward_gap (); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_working_mktime=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_working_mktime=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_working_mktime" >&5 +echo "${ECHO_T}$ac_cv_func_working_mktime" >&6 +if test $ac_cv_func_working_mktime = no; then + case $LIBOBJS in + "mktime.$ac_objext" | \ + *" mktime.$ac_objext" | \ + "mktime.$ac_objext "* | \ + *" mktime.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS mktime.$ac_objext" ;; +esac + +fi + + + + if test $ac_cv_func_working_mktime = no; then + +cat >>confdefs.h <<\_ACEOF +#define mktime rpl_mktime +_ACEOF + + : + fi + + + nanosleep_save_libs=$LIBS + + # Solaris 2.5.1 needs -lposix4 to get the nanosleep function. + # Solaris 7 prefers the library name -lrt to the obsolescent name -lposix4. + echo "$as_me:$LINENO: checking for library containing nanosleep" >&5 +echo $ECHO_N "checking for library containing nanosleep... $ECHO_C" >&6 +if test "${ac_cv_search_nanosleep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_nanosleep=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char nanosleep (); +int +main () +{ +nanosleep (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_nanosleep="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_nanosleep" = no; then + for ac_lib in rt posix4; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char nanosleep (); +int +main () +{ +nanosleep (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_nanosleep="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_nanosleep" >&5 +echo "${ECHO_T}$ac_cv_search_nanosleep" >&6 +if test "$ac_cv_search_nanosleep" != no; then + test "$ac_cv_search_nanosleep" = "none required" || LIBS="$ac_cv_search_nanosleep $LIBS" + test "$ac_cv_search_nanosleep" = "none required" || + LIB_NANOSLEEP=$ac_cv_search_nanosleep +fi + + + + echo "$as_me:$LINENO: checking whether nanosleep works" >&5 +echo $ECHO_N "checking whether nanosleep works... $ECHO_C" >&6 +if test "${jm_cv_func_nanosleep_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + + : + + + + + + if test "$cross_compiling" = yes; then + jm_cv_func_nanosleep_works=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# if TIME_WITH_SYS_TIME +# include +# include +# else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +# endif + + int + main () + { + struct timespec ts_sleep, ts_remaining; + ts_sleep.tv_sec = 0; + ts_sleep.tv_nsec = 1; + exit (nanosleep (&ts_sleep, &ts_remaining) == 0 ? 0 : 1); + } + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + jm_cv_func_nanosleep_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +jm_cv_func_nanosleep_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + +fi +echo "$as_me:$LINENO: result: $jm_cv_func_nanosleep_works" >&5 +echo "${ECHO_T}$jm_cv_func_nanosleep_works" >&6 + if test $jm_cv_func_nanosleep_works = no; then + case $LIBOBJS in + "nanosleep.$ac_objext" | \ + *" nanosleep.$ac_objext" | \ + "nanosleep.$ac_objext "* | \ + *" nanosleep.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS nanosleep.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define nanosleep rpl_nanosleep +_ACEOF + + + + : + + + + + + + fi + + LIBS=$nanosleep_save_libs + + + +for ac_func in strerror +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + if test $ac_cv_func_strerror = no; then + + : + + fi + + + + echo "$as_me:$LINENO: checking whether tzset clobbers localtime buffer" >&5 +echo $ECHO_N "checking whether tzset clobbers localtime buffer... $ECHO_C" >&6 +if test "${gl_cv_func_tzset_clobber+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + if test "$cross_compiling" = yes; then + gl_cv_func_tzset_clobber=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include + +int +main () +{ + time_t t1 = 853958121; + struct tm *p, s; + putenv ("TZ=GMT0"); + p = localtime (&t1); + s = *p; + putenv ("TZ=EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00"); + tzset (); + exit (p->tm_year != s.tm_year + || p->tm_mon != s.tm_mon + || p->tm_mday != s.tm_mday + || p->tm_hour != s.tm_hour + || p->tm_min != s.tm_min + || p->tm_sec != s.tm_sec); +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gl_cv_func_tzset_clobber=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +gl_cv_func_tzset_clobber=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $gl_cv_func_tzset_clobber" >&5 +echo "${ECHO_T}$gl_cv_func_tzset_clobber" >&6 + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_RUN_TZSET_TEST 1 +_ACEOF + + + if test $gl_cv_func_tzset_clobber = yes; then + + case $LIBOBJS in + "gettimeofday.$ac_objext" | \ + *" gettimeofday.$ac_objext" | \ + "gettimeofday.$ac_objext "* | \ + *" gettimeofday.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS gettimeofday.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define gmtime rpl_gmtime +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define localtime rpl_localtime +_ACEOF + + + + +cat >>confdefs.h <<\_ACEOF +#define tzset rpl_tzset +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define TZSET_CLOBBERS_LOCALTIME_BUFFER 1 +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking for signed" >&5 +echo $ECHO_N "checking for signed... $ECHO_C" >&6 +if test "${bh_cv_c_signed+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +signed char x; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + bh_cv_c_signed=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +bh_cv_c_signed=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $bh_cv_c_signed" >&5 +echo "${ECHO_T}$bh_cv_c_signed" >&6 + if test $bh_cv_c_signed = no; then + +cat >>confdefs.h <<\_ACEOF +#define signed +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking for long long" >&5 +echo $ECHO_N "checking for long long... $ECHO_C" >&6 +if test "${ac_cv_type_long_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +long long ll = 1LL; int i = 63; +int +main () +{ +long long llmax = (long long) -1; + return ll << i | ll >> i | llmax / ll | llmax % ll; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long_long=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long_long=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long_long" >&5 +echo "${ECHO_T}$ac_cv_type_long_long" >&6 + if test $ac_cv_type_long_long = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LONG_LONG 1 +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking for long double" >&5 +echo $ECHO_N "checking for long double... $ECHO_C" >&6 +if test "${gt_cv_c_long_double+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$GCC" = yes; then + gt_cv_c_long_double=yes + else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + /* The Stardent Vistra knows sizeof(long double), but does not support it. */ + long double foo = 0.0; + /* On Ultrix 4.3 cc, long double is 4 and double is 8. */ + int array [2*(sizeof(long double) >= sizeof(double)) - 1]; + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gt_cv_c_long_double=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gt_cv_c_long_double=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + fi +fi +echo "$as_me:$LINENO: result: $gt_cv_c_long_double" >&5 +echo "${ECHO_T}$gt_cv_c_long_double" >&6 + if test $gt_cv_c_long_double = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LONG_DOUBLE 1 +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking for wchar_t" >&5 +echo $ECHO_N "checking for wchar_t... $ECHO_C" >&6 +if test "${gt_cv_c_wchar_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + wchar_t foo = (wchar_t)'\0'; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gt_cv_c_wchar_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gt_cv_c_wchar_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $gt_cv_c_wchar_t" >&5 +echo "${ECHO_T}$gt_cv_c_wchar_t" >&6 + if test $gt_cv_c_wchar_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WCHAR_T 1 +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking for wint_t" >&5 +echo $ECHO_N "checking for wint_t... $ECHO_C" >&6 +if test "${gt_cv_c_wint_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + wint_t foo = (wchar_t)'\0'; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gt_cv_c_wint_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gt_cv_c_wint_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $gt_cv_c_wint_t" >&5 +echo "${ECHO_T}$gt_cv_c_wint_t" >&6 + if test $gt_cv_c_wint_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WINT_T 1 +_ACEOF + + fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + + + + + echo "$as_me:$LINENO: checking for intmax_t" >&5 +echo $ECHO_N "checking for intmax_t... $ECHO_C" >&6 +if test "${gt_cv_c_intmax_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +#if HAVE_STDINT_H_WITH_UINTMAX +#include +#endif +#if HAVE_INTTYPES_H_WITH_UINTMAX +#include +#endif + +int +main () +{ +intmax_t x = -1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gt_cv_c_intmax_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gt_cv_c_intmax_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $gt_cv_c_intmax_t" >&5 +echo "${ECHO_T}$gt_cv_c_intmax_t" >&6 + if test $gt_cv_c_intmax_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_INTMAX_T 1 +_ACEOF + + else + + test $ac_cv_type_long_long = yes \ + && ac_type='long long' \ + || ac_type='long' + +cat >>confdefs.h <<_ACEOF +#define intmax_t $ac_type +_ACEOF + + fi + + + +for ac_func in vasnprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + if test $ac_cv_func_vasnprintf = no; then + case $LIBOBJS in + "printf-args.$ac_objext" | \ + *" printf-args.$ac_objext" | \ + "printf-args.$ac_objext "* | \ + *" printf-args.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS printf-args.$ac_objext" ;; +esac + + case $LIBOBJS in + "printf-parse.$ac_objext" | \ + *" printf-parse.$ac_objext" | \ + "printf-parse.$ac_objext "* | \ + *" printf-parse.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS printf-parse.$ac_objext" ;; +esac + + case $LIBOBJS in + "asnprintf.$ac_objext" | \ + *" asnprintf.$ac_objext" | \ + "asnprintf.$ac_objext "* | \ + *" asnprintf.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS asnprintf.$ac_objext" ;; +esac + + + + + + + + + + + + + + + echo "$as_me:$LINENO: checking for ptrdiff_t" >&5 +echo $ECHO_N "checking for ptrdiff_t... $ECHO_C" >&6 +if test "${ac_cv_type_ptrdiff_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((ptrdiff_t *) 0) + return 0; +if (sizeof (ptrdiff_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_ptrdiff_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_ptrdiff_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_ptrdiff_t" >&5 +echo "${ECHO_T}$ac_cv_type_ptrdiff_t" >&6 +if test $ac_cv_type_ptrdiff_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_PTRDIFF_T 1 +_ACEOF + + +fi + + + + + + + + + + + +for ac_func in snprintf wcslen +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + + + fi + + + + + : + + + + + + + + + + + : + + + + + + echo "$as_me:$LINENO: checking for struct timespec" >&5 +echo $ECHO_N "checking for struct timespec... $ECHO_C" >&6 +if test "${fu_cv_sys_struct_timespec+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# if TIME_WITH_SYS_TIME +# include +# include +# else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +# endif + +int +main () +{ +static struct timespec x; x.tv_sec = x.tv_nsec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + fu_cv_sys_struct_timespec=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fu_cv_sys_struct_timespec=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $fu_cv_sys_struct_timespec" >&5 +echo "${ECHO_T}$fu_cv_sys_struct_timespec" >&6 + + if test $fu_cv_sys_struct_timespec = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STRUCT_TIMESPEC 1 +_ACEOF + + fi + + echo "$as_me:$LINENO: checking for nanoseconds member of struct stat.st_mtim" >&5 +echo $ECHO_N "checking for nanoseconds member of struct stat.st_mtim... $ECHO_C" >&6 +if test "${ac_cv_struct_st_mtim_nsec+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_CPPFLAGS="$CPPFLAGS" + ac_cv_struct_st_mtim_nsec=no + # tv_nsec -- the usual case + # _tv_nsec -- Solaris 2.6, if + # (defined _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED == 1 + # && !defined __EXTENSIONS__) + # st__tim.tv_nsec -- UnixWare 2.1.2 + for ac_val in tv_nsec _tv_nsec st__tim.tv_nsec; do + CPPFLAGS="$ac_save_CPPFLAGS -DST_MTIM_NSEC=$ac_val" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +int +main () +{ +struct stat s; s.st_mtim.ST_MTIM_NSEC; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_st_mtim_nsec=$ac_val; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + CPPFLAGS="$ac_save_CPPFLAGS" +fi +echo "$as_me:$LINENO: result: $ac_cv_struct_st_mtim_nsec" >&5 +echo "${ECHO_T}$ac_cv_struct_st_mtim_nsec" >&6 + + if test $ac_cv_struct_st_mtim_nsec != no; then + +cat >>confdefs.h <<_ACEOF +#define ST_MTIM_NSEC $ac_cv_struct_st_mtim_nsec +_ACEOF + + fi + + + + + + echo "$as_me:$LINENO: checking whether nanosleep is declared" >&5 +echo $ECHO_N "checking whether nanosleep is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_nanosleep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef nanosleep + char *p = (char *) nanosleep; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_nanosleep=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_nanosleep=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_nanosleep" >&5 +echo "${ECHO_T}$ac_cv_have_decl_nanosleep" >&6 +if test $ac_cv_have_decl_nanosleep = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_NANOSLEEP 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_NANOSLEEP 0 +_ACEOF + + +fi + + + + + # getdate.y works with bison only. + +YACC=${YACC-"${am_missing_run}bison -y"} + + + + + + + + + + + + # dnl Persuade glibc to declare these functions. + + + # Solaris 2.5.1 needs -lposix4 to get the clock_gettime function. + # Solaris 7 prefers the library name -lrt to the obsolescent name -lposix4. + + # Save and restore LIBS so e.g., -lrt, isn't added to it. Otherwise, *all* + # programs in the package would end up linked with that potentially-shared + # library, inducing unnecessary run-time overhead. + fetish_saved_libs=$LIBS + echo "$as_me:$LINENO: checking for library containing clock_gettime" >&5 +echo $ECHO_N "checking for library containing clock_gettime... $ECHO_C" >&6 +if test "${ac_cv_search_clock_gettime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_clock_gettime=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char clock_gettime (); +int +main () +{ +clock_gettime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_clock_gettime="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_clock_gettime" = no; then + for ac_lib in rt posix4; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char clock_gettime (); +int +main () +{ +clock_gettime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_clock_gettime="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_clock_gettime" >&5 +echo "${ECHO_T}$ac_cv_search_clock_gettime" >&6 +if test "$ac_cv_search_clock_gettime" != no; then + test "$ac_cv_search_clock_gettime" = "none required" || LIBS="$ac_cv_search_clock_gettime $LIBS" + test "$ac_cv_search_clock_gettime" = "none required" || + LIB_CLOCK_GETTIME=$ac_cv_search_clock_gettime +fi + + + + +for ac_func in clock_gettime clock_settime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + LIBS=$fetish_saved_libs + + + + + + + + + + : + + + + + + echo "$as_me:$LINENO: checking for struct tm.tm_zone" >&5 +echo $ECHO_N "checking for struct tm.tm_zone... $ECHO_C" >&6 +if test "${ac_cv_member_struct_tm_tm_zone+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_cv_struct_tm> + + +int +main () +{ +static struct tm ac_aggr; +if (ac_aggr.tm_zone) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_tm_tm_zone=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_cv_struct_tm> + + +int +main () +{ +static struct tm ac_aggr; +if (sizeof ac_aggr.tm_zone) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_tm_tm_zone=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_tm_tm_zone=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_tm_tm_zone" >&5 +echo "${ECHO_T}$ac_cv_member_struct_tm_tm_zone" >&6 +if test $ac_cv_member_struct_tm_tm_zone = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_TM_TM_ZONE 1 +_ACEOF + + +fi + +if test "$ac_cv_member_struct_tm_tm_zone" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TM_ZONE 1 +_ACEOF + +else + echo "$as_me:$LINENO: checking for tzname" >&5 +echo $ECHO_N "checking for tzname... $ECHO_C" >&6 +if test "${ac_cv_var_tzname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifndef tzname /* For SGI. */ +extern char *tzname[]; /* RS6000 and others reject char **tzname. */ +#endif + +int +main () +{ +atoi(*tzname); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_var_tzname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_var_tzname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_var_tzname" >&5 +echo "${ECHO_T}$ac_cv_var_tzname" >&6 + if test $ac_cv_var_tzname = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TZNAME 1 +_ACEOF + + fi +fi + + + + + + # Avoid multiple inclusions of getndelim2.o into LIBOBJS. + # This hack won't be needed after gnulib requires Autoconf 2.58 or later. + case " $LIBOBJS " in + *" getndelim2.$ac_objext "* ) ;; + *) case $LIBOBJS in + "getndelim2.$ac_objext" | \ + *" getndelim2.$ac_objext" | \ + "getndelim2.$ac_objext "* | \ + *" getndelim2.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getndelim2.$ac_objext" ;; +esac +;; + esac + + + + + + + + : + + + : + + + + + + + + + + + + : + + + + + + + + + + + + + + + : + + + + + + + + + + + + + + + + + + + + + + + + + # Assume we'll default to using the included regex.c. + ac_use_included_regex=yes + + # However, if the system regex support is good enough that it passes the + # the following run test, then default to *not* using the included regex.c. + # If cross compiling, assume the test would fail and use the included + # regex.c. The first failing regular expression is from `Spencer ere + # test #75' in grep-2.3. + echo "$as_me:$LINENO: checking for working re_compile_pattern" >&5 +echo $ECHO_N "checking for working re_compile_pattern... $ECHO_C" >&6 +if test "${jm_cv_func_working_re_compile_pattern+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + jm_cv_func_working_re_compile_pattern=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + int + main () + { + static struct re_pattern_buffer regex; + const char *s; + struct re_registers regs; + re_set_syntax (RE_SYNTAX_POSIX_EGREP); + memset (®ex, 0, sizeof (regex)); + s = re_compile_pattern ("a[[:]:]]b\n", 9, ®ex); + /* This should fail with _Invalid character class name_ error. */ + if (!s) + exit (1); + + /* This should succeed, but doesn't for e.g. glibc-2.1.3. */ + memset (®ex, 0, sizeof (regex)); + s = re_compile_pattern ("{1", 2, ®ex); + + if (s) + exit (1); + + /* The following example is derived from a problem report + against gawk from Jorge Stolfi . */ + memset (®ex, 0, sizeof (regex)); + s = re_compile_pattern ("[an\371]*n", 7, ®ex); + if (s) + exit (1); + + /* This should match, but doesn't for e.g. glibc-2.2.1. */ + if (re_match (®ex, "an", 2, 0, ®s) != 2) + exit (1); + + memset (®ex, 0, sizeof (regex)); + s = re_compile_pattern ("x", 1, ®ex); + if (s) + exit (1); + + /* The version of regex.c in e.g. GNU libc-2.2.93 didn't + work with a negative RANGE argument. */ + if (re_search (®ex, "wxy", 3, 2, -2, ®s) != 1) + exit (1); + + exit (0); + } + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + jm_cv_func_working_re_compile_pattern=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +jm_cv_func_working_re_compile_pattern=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $jm_cv_func_working_re_compile_pattern" >&5 +echo "${ECHO_T}$jm_cv_func_working_re_compile_pattern" >&6 + if test $jm_cv_func_working_re_compile_pattern = yes; then + ac_use_included_regex=no + fi + + test -n "lib/regex.c" || { { echo "$as_me:$LINENO: error: missing argument" >&5 +echo "$as_me: error: missing argument" >&2;} + { (exit 1); exit 1; }; } + + + +# Check whether --with-included-regex or --without-included-regex was given. +if test "${with_included_regex+set}" = set; then + withval="$with_included_regex" + jm_with_regex=$withval +else + jm_with_regex=$ac_use_included_regex +fi; + if test "$jm_with_regex" = yes; then + case $LIBOBJS in + "regex.$ac_objext" | \ + *" regex.$ac_objext" | \ + "regex.$ac_objext "* | \ + *" regex.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS regex.$ac_objext" ;; +esac + + + + + + + + + + : + + + + + + + + + + : + + + + + + + + + +for ac_func in btowc +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + fi + + + + + + + + + echo "$as_me:$LINENO: checking whether localtime_r is compatible with its POSIX signature" >&5 +echo $ECHO_N "checking whether localtime_r is compatible with its POSIX signature... $ECHO_C" >&6 +if test "${gl_cv_time_r_posix+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +/* We don't need to append 'restrict's to the argument types, + even though the POSIX signature has the 'restrict's, + since C99 says they can't affect type compatibility. */ + struct tm * (*ptr) (time_t const *, struct tm *) = localtime_r; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gl_cv_time_r_posix=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gl_cv_time_r_posix=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $gl_cv_time_r_posix" >&5 +echo "${ECHO_T}$gl_cv_time_r_posix" >&6 + if test $gl_cv_time_r_posix = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TIME_R_POSIX 1 +_ACEOF + + else + case $LIBOBJS in + "time_r.$ac_objext" | \ + *" time_r.$ac_objext" | \ + "time_r.$ac_objext "* | \ + *" time_r.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS time_r.$ac_objext" ;; +esac + + + : + + fi + + + + + : + + + + + + + + + + + : + + + + + + echo "$as_me:$LINENO: checking for struct timespec" >&5 +echo $ECHO_N "checking for struct timespec... $ECHO_C" >&6 +if test "${fu_cv_sys_struct_timespec+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# if TIME_WITH_SYS_TIME +# include +# include +# else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +# endif + +int +main () +{ +static struct timespec x; x.tv_sec = x.tv_nsec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + fu_cv_sys_struct_timespec=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fu_cv_sys_struct_timespec=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $fu_cv_sys_struct_timespec" >&5 +echo "${ECHO_T}$fu_cv_sys_struct_timespec" >&6 + + if test $fu_cv_sys_struct_timespec = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STRUCT_TIMESPEC 1 +_ACEOF + + fi + + echo "$as_me:$LINENO: checking for nanoseconds member of struct stat.st_mtim" >&5 +echo $ECHO_N "checking for nanoseconds member of struct stat.st_mtim... $ECHO_C" >&6 +if test "${ac_cv_struct_st_mtim_nsec+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_CPPFLAGS="$CPPFLAGS" + ac_cv_struct_st_mtim_nsec=no + # tv_nsec -- the usual case + # _tv_nsec -- Solaris 2.6, if + # (defined _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED == 1 + # && !defined __EXTENSIONS__) + # st__tim.tv_nsec -- UnixWare 2.1.2 + for ac_val in tv_nsec _tv_nsec st__tim.tv_nsec; do + CPPFLAGS="$ac_save_CPPFLAGS -DST_MTIM_NSEC=$ac_val" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +int +main () +{ +struct stat s; s.st_mtim.ST_MTIM_NSEC; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_st_mtim_nsec=$ac_val; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + CPPFLAGS="$ac_save_CPPFLAGS" +fi +echo "$as_me:$LINENO: result: $ac_cv_struct_st_mtim_nsec" >&5 +echo "${ECHO_T}$ac_cv_struct_st_mtim_nsec" >&6 + + if test $ac_cv_struct_st_mtim_nsec != no; then + +cat >>confdefs.h <<_ACEOF +#define ST_MTIM_NSEC $ac_cv_struct_st_mtim_nsec +_ACEOF + + fi + + + + + + echo "$as_me:$LINENO: checking whether nanosleep is declared" >&5 +echo $ECHO_N "checking whether nanosleep is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_nanosleep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef nanosleep + char *p = (char *) nanosleep; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_nanosleep=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_nanosleep=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_nanosleep" >&5 +echo "${ECHO_T}$ac_cv_have_decl_nanosleep" >&6 +if test $ac_cv_have_decl_nanosleep = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_NANOSLEEP 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_NANOSLEEP 0 +_ACEOF + + +fi + + + +echo "$as_me:$LINENO: checking for inline" >&5 +echo $ECHO_N "checking for inline... $ECHO_C" >&6 +if test "${ac_cv_c_inline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_inline=$ac_kw; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 +echo "${ECHO_T}$ac_cv_c_inline" >&6 + + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + + +for ac_header in stdlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 +echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6 +if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_malloc_0_nonnull=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if STDC_HEADERS || HAVE_STDLIB_H +# include +#else +char *malloc (); +#endif + +int +main () +{ +exit (malloc (0) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_malloc_0_nonnull=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_malloc_0_nonnull=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 +echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6 +if test $ac_cv_func_malloc_0_nonnull = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 1 +_ACEOF + +else + cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 0 +_ACEOF + + case $LIBOBJS in + "malloc.$ac_objext" | \ + *" malloc.$ac_objext" | \ + "malloc.$ac_objext "* | \ + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define malloc rpl_malloc +_ACEOF + +fi + + + + + + if test X"$ac_cv_func_malloc_0_nonnull" = Xno || test X"$ac_cv_func_malloc_works" = Xno; then + + : + + fi + + +for ac_header in stdlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking for GNU libc compatible realloc" >&5 +echo $ECHO_N "checking for GNU libc compatible realloc... $ECHO_C" >&6 +if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_realloc_0_nonnull=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if STDC_HEADERS || HAVE_STDLIB_H +# include +#else +char *realloc (); +#endif + +int +main () +{ +exit (realloc (0, 0) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_realloc_0_nonnull=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_realloc_0_nonnull=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_realloc_0_nonnull" >&5 +echo "${ECHO_T}$ac_cv_func_realloc_0_nonnull" >&6 +if test $ac_cv_func_realloc_0_nonnull = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_REALLOC 1 +_ACEOF + +else + cat >>confdefs.h <<\_ACEOF +#define HAVE_REALLOC 0 +_ACEOF + + case $LIBOBJS in + "realloc.$ac_objext" | \ + *" realloc.$ac_objext" | \ + "realloc.$ac_objext "* | \ + *" realloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS realloc.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define realloc rpl_realloc +_ACEOF + +fi + + + + + + if test X"$ac_cv_func_realloc_0_nonnull" = Xno || test X"$ac_cv_func_realloc_works" = Xno; then + + : + + fi + + + + : + + + + + + : + + + : + + + + +for ac_header in stdint.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + echo "$as_me:$LINENO: checking for SIZE_MAX" >&5 +echo $ECHO_N "checking for SIZE_MAX... $ECHO_C" >&6 + result= + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#if HAVE_STDINT_H +#include +#endif +#ifdef SIZE_MAX +Found it +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "Found it" >/dev/null 2>&1; then + result=yes +fi +rm -f conftest* + + if test -z "$result"; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 / 10) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 / 10) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 / 10) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 / 10) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 / 10) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) res_hi=$ac_lo;; +'') result=? ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +long longval () { return ~(size_t)0 / 10; } +unsigned long ulongval () { return ~(size_t)0 / 10; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if ((~(size_t)0 / 10) < 0) + { + long i = longval (); + if (i != (~(size_t)0 / 10)) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != (~(size_t)0 / 10)) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + res_hi=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +result=? +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 % 10) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 % 10) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 % 10) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 % 10) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((~(size_t)0 % 10) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) res_lo=$ac_lo;; +'') result=? ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +long longval () { return ~(size_t)0 % 10; } +unsigned long ulongval () { return ~(size_t)0 % 10; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if ((~(size_t)0 % 10) < 0) + { + long i = longval (); + if (i != (~(size_t)0 % 10)) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != (~(size_t)0 % 10)) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + res_lo=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +result=? +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((sizeof (size_t) <= sizeof (unsigned int)) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((sizeof (size_t) <= sizeof (unsigned int)) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((sizeof (size_t) <= sizeof (unsigned int)) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((sizeof (size_t) <= sizeof (unsigned int)) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +static int test_array [1 - 2 * !((sizeof (size_t) <= sizeof (unsigned int)) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) fits_in_uint=$ac_lo;; +'') result=? ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +long longval () { return sizeof (size_t) <= sizeof (unsigned int); } +unsigned long ulongval () { return sizeof (size_t) <= sizeof (unsigned int); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if ((sizeof (size_t) <= sizeof (unsigned int)) < 0) + { + long i = longval (); + if (i != (sizeof (size_t) <= sizeof (unsigned int))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != (sizeof (size_t) <= sizeof (unsigned int))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + fits_in_uint=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +result=? +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val + if test "$fits_in_uint" = 1; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + extern size_t foo; + extern unsigned long foo; + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + fits_in_uint=0 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test -z "$result"; then + if test "$fits_in_uint" = 1; then + result="$res_hi$res_lo"U + else + result="$res_hi$res_lo"UL + fi + else + result='~(size_t)0' + fi + fi + echo "$as_me:$LINENO: result: $result" >&5 +echo "${ECHO_T}$result" >&6 + if test "$result" != yes; then + +cat >>confdefs.h <<_ACEOF +#define SIZE_MAX $result +_ACEOF + + fi + + + + + +for ac_header in stdint.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 +echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6 +if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.sym conftest.file +echo >conftest.file +if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_dereferences_slashed_symlink=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + /* Linux will dereference the symlink and fail. + That is better in the sense that it means we will not + have to compile and use the lstat wrapper. */ + exit (lstat ("conftest.sym/", &sbuf) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_dereferences_slashed_symlink=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +else + # If the `ln -s' command failed, then we probably don't even + # have an lstat function. + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f conftest.sym conftest.file + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6 + +test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && + +cat >>confdefs.h <<_ACEOF +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +_ACEOF + + +if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then + case $LIBOBJS in + "lstat.$ac_objext" | \ + *" lstat.$ac_objext" | \ + "lstat.$ac_objext "* | \ + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; +esac + +fi + + + + + + + + + echo "$as_me:$LINENO: checking whether free is declared" >&5 +echo $ECHO_N "checking whether free is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_free+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef free + char *p = (char *) free; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_free=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_free=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_free" >&5 +echo "${ECHO_T}$ac_cv_have_decl_free" >&6 +if test $ac_cv_have_decl_free = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FREE 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FREE 0 +_ACEOF + + +fi + + + + + echo "$as_me:$LINENO: checking whether lstat accepts an empty string" >&5 +echo $ECHO_N "checking whether lstat accepts an empty string... $ECHO_C" >&6 +if test "${ac_cv_func_lstat_empty_string_bug+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_empty_string_bug=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + exit (lstat ("", &sbuf) ? 1 : 0); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_empty_string_bug=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_empty_string_bug=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_lstat_empty_string_bug" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_empty_string_bug" >&6 +if test $ac_cv_func_lstat_empty_string_bug = yes; then + case $LIBOBJS in + "lstat.$ac_objext" | \ + *" lstat.$ac_objext" | \ + "lstat.$ac_objext "* | \ + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; +esac + + +cat >>confdefs.h <<_ACEOF +#define HAVE_LSTAT_EMPTY_STRING_BUG 1 +_ACEOF + +fi + + if test $ac_cv_func_lstat_empty_string_bug = yes || + test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then + + + + : + + + + + + + : + + + + + + + fi + + + + if test X"$ac_cv_func_malloc_0_nonnull" = Xno || test X"$ac_cv_func_malloc_works" = Xno; then + + : + + fi + + + + if test X"$ac_cv_func_realloc_0_nonnull" = Xno || test X"$ac_cv_func_realloc_works" = Xno; then + + : + + fi + + + echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5 +echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6 +if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_stat_empty_string_bug=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + exit (stat ("", &sbuf) ? 1 : 0); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_stat_empty_string_bug=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_stat_empty_string_bug=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5 +echo "${ECHO_T}$ac_cv_func_stat_empty_string_bug" >&6 +if test $ac_cv_func_stat_empty_string_bug = yes; then + case $LIBOBJS in + "stat.$ac_objext" | \ + *" stat.$ac_objext" | \ + "stat.$ac_objext "* | \ + *" stat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS stat.$ac_objext" ;; +esac + + +cat >>confdefs.h <<_ACEOF +#define HAVE_STAT_EMPTY_STRING_BUG 1 +_ACEOF + +fi + + if test $ac_cv_func_stat_empty_string_bug = yes; then + + : + + fi + + + + + + if test $jm_ac_cv_header_inttypes_h = no && test $jm_ac_cv_header_stdint_h = no; then + + test $ac_cv_type_long_long = yes \ + && ac_type='long long' \ + || ac_type='long' + +cat >>confdefs.h <<_ACEOF +#define intmax_t $ac_type +_ACEOF + + else + +cat >>confdefs.h <<\_ACEOF +#define HAVE_INTMAX_T 1 +_ACEOF + + fi + + + + + + + + + case $LIBOBJS in + "getpass.$ac_objext" | \ + *" getpass.$ac_objext" | \ + "getpass.$ac_objext "* | \ + *" getpass.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getpass.$ac_objext" ;; +esac + + + : + + +cat >>confdefs.h <<\_ACEOF +#define getpass gnu_getpass +_ACEOF + + + +cat >>confdefs.h <<\_ACEOF +#define getpass cvs_getpass +_ACEOF + + +# +# End GNULIB stuff. +# + + + +# Check for function existance. + + + + + + + + + + + + + + + + + + + + + + + + + + + +for ac_func in \ + fchdir \ + fchmod \ + fsync \ + ftime \ + geteuid \ + getgroups \ + getpagesize \ + gettimeofday \ + initgroups \ + login \ + logout \ + mknod \ + putenv \ + readlink \ + regcomp \ + regerror \ + regexec \ + regfree \ + sigaction \ + sigblock \ + sigprocmask \ + sigsetmask \ + sigvec \ + timezone \ + tzset \ + vprintf \ + wait3 \ + +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +HAVE_PUTENV=$ac_cv_func_putenv + + +if test $cross_compiling = yes ; then + +cat >>confdefs.h <<\_ACEOF +#define CROSS_COMPILING 1 +_ACEOF + +else + echo "$as_me:$LINENO: checking for char" >&5 +echo $ECHO_N "checking for char... $ECHO_C" >&6 +if test "${ac_cv_type_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((char *) 0) + return 0; +if (sizeof (char)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_char=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_char=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_char" >&5 +echo "${ECHO_T}$ac_cv_type_char" >&6 + +echo "$as_me:$LINENO: checking size of char" >&5 +echo $ECHO_N "checking size of char... $ECHO_C" >&6 +if test "${ac_cv_sizeof_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_char" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (char))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (char))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (char))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (char))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (char))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_char=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (char), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (char), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (char)); } +unsigned long ulongval () { return (long) (sizeof (char)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (char))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (char)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (char)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_char=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (char), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (char), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_char=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_char" >&5 +echo "${ECHO_T}$ac_cv_sizeof_char" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_CHAR $ac_cv_sizeof_char +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized char" >&5 +echo $ECHO_N "checking for uniquely sized char... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_char)" >/dev/null ; then + ccvs_cv_unique_int_type_char=no + else + ccvs_cv_unique_int_type_char=yes\($ac_cv_sizeof_char\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_char" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_char" >&6 + if test $ccvs_cv_unique_int_type_char != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_CHAR 1 +_ACEOF + + fi + echo "$as_me:$LINENO: checking for short" >&5 +echo $ECHO_N "checking for short... $ECHO_C" >&6 +if test "${ac_cv_type_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((short *) 0) + return 0; +if (sizeof (short)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_short=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_short=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_short" >&5 +echo "${ECHO_T}$ac_cv_type_short" >&6 + +echo "$as_me:$LINENO: checking size of short" >&5 +echo $ECHO_N "checking size of short... $ECHO_C" >&6 +if test "${ac_cv_sizeof_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_short" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (short))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_short=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (short), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (short), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (short)); } +unsigned long ulongval () { return (long) (sizeof (short)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (short))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (short)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (short)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_short=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (short), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (short), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_short=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_short" >&5 +echo "${ECHO_T}$ac_cv_sizeof_short" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_SHORT $ac_cv_sizeof_short +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized short" >&5 +echo $ECHO_N "checking for uniquely sized short... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_short)" >/dev/null ; then + ccvs_cv_unique_int_type_short=no + else + ccvs_cv_unique_int_type_short=yes\($ac_cv_sizeof_short\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_short" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_short" >&6 + if test $ccvs_cv_unique_int_type_short != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_SHORT 1 +_ACEOF + + fi + echo "$as_me:$LINENO: checking for int" >&5 +echo $ECHO_N "checking for int... $ECHO_C" >&6 +if test "${ac_cv_type_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((int *) 0) + return 0; +if (sizeof (int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_int=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_int=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_int" >&5 +echo "${ECHO_T}$ac_cv_type_int" >&6 + +echo "$as_me:$LINENO: checking size of int" >&5 +echo $ECHO_N "checking size of int... $ECHO_C" >&6 +if test "${ac_cv_sizeof_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_int" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (int))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_int=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (int), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (int), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (int)); } +unsigned long ulongval () { return (long) (sizeof (int)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (int))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (int)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (int)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_int=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (int), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (int), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_int=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_int" >&5 +echo "${ECHO_T}$ac_cv_sizeof_int" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INT $ac_cv_sizeof_int +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized int" >&5 +echo $ECHO_N "checking for uniquely sized int... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_int)" >/dev/null ; then + ccvs_cv_unique_int_type_int=no + else + ccvs_cv_unique_int_type_int=yes\($ac_cv_sizeof_int\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_int" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_int" >&6 + if test $ccvs_cv_unique_int_type_int != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_INT 1 +_ACEOF + + fi + echo "$as_me:$LINENO: checking for long" >&5 +echo $ECHO_N "checking for long... $ECHO_C" >&6 +if test "${ac_cv_type_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((long *) 0) + return 0; +if (sizeof (long)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long" >&5 +echo "${ECHO_T}$ac_cv_type_long" >&6 + +echo "$as_me:$LINENO: checking size of long" >&5 +echo $ECHO_N "checking size of long... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_long=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (long), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (long)); } +unsigned long ulongval () { return (long) (sizeof (long)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (long))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (long)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (long)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (long), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_long" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG $ac_cv_sizeof_long +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized long" >&5 +echo $ECHO_N "checking for uniquely sized long... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_long)" >/dev/null ; then + ccvs_cv_unique_int_type_long=no + else + ccvs_cv_unique_int_type_long=yes\($ac_cv_sizeof_long\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_long" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_long" >&6 + if test $ccvs_cv_unique_int_type_long != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_LONG 1 +_ACEOF + + fi + if test $ac_cv_type_long_long != no; then + echo "$as_me:$LINENO: checking for long long" >&5 +echo $ECHO_N "checking for long long... $ECHO_C" >&6 +if test "${ac_cv_type_long_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((long long *) 0) + return 0; +if (sizeof (long long)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long_long=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long_long=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long_long" >&5 +echo "${ECHO_T}$ac_cv_type_long_long" >&6 + +echo "$as_me:$LINENO: checking size of long long" >&5 +echo $ECHO_N "checking size of long long... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long_long" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long long))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long long))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long long))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long long))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long long))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_long_long=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (long long), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long long), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (long long)); } +unsigned long ulongval () { return (long) (sizeof (long long)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (long long))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (long long)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (long long)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long_long=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (long long), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long long), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long_long=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_long_long" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long_long" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized long long" >&5 +echo $ECHO_N "checking for uniquely sized long long... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_long_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_long_long)" >/dev/null ; then + ccvs_cv_unique_int_type_long_long=no + else + ccvs_cv_unique_int_type_long_long=yes\($ac_cv_sizeof_long_long\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_long_long" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_long_long" >&6 + if test $ccvs_cv_unique_int_type_long_long != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_LONG_LONG 1 +_ACEOF + + fi + fi + echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 + +echo "$as_me:$LINENO: checking size of size_t" >&5 +echo $ECHO_N "checking size of size_t... $ECHO_C" >&6 +if test "${ac_cv_sizeof_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_size_t" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_size_t=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (size_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (size_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (size_t)); } +unsigned long ulongval () { return (long) (sizeof (size_t)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (size_t))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (size_t)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (size_t)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_size_t=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (size_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (size_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_size_t=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_size_t" >&5 +echo "${ECHO_T}$ac_cv_sizeof_size_t" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized size_t" >&5 +echo $ECHO_N "checking for uniquely sized size_t... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_size_t)" >/dev/null ; then + ccvs_cv_unique_int_type_size_t=no + else + ccvs_cv_unique_int_type_size_t=yes\($ac_cv_sizeof_size_t\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_size_t" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_size_t" >&6 + if test $ccvs_cv_unique_int_type_size_t != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_SIZE_T 1 +_ACEOF + + fi + echo "$as_me:$LINENO: checking for ptrdiff_t" >&5 +echo $ECHO_N "checking for ptrdiff_t... $ECHO_C" >&6 +if test "${ac_cv_type_ptrdiff_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((ptrdiff_t *) 0) + return 0; +if (sizeof (ptrdiff_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_ptrdiff_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_ptrdiff_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_ptrdiff_t" >&5 +echo "${ECHO_T}$ac_cv_type_ptrdiff_t" >&6 + +echo "$as_me:$LINENO: checking size of ptrdiff_t" >&5 +echo $ECHO_N "checking size of ptrdiff_t... $ECHO_C" >&6 +if test "${ac_cv_sizeof_ptrdiff_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_ptrdiff_t" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (ptrdiff_t))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (ptrdiff_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (ptrdiff_t))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (ptrdiff_t))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (ptrdiff_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_ptrdiff_t=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (ptrdiff_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (ptrdiff_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (ptrdiff_t)); } +unsigned long ulongval () { return (long) (sizeof (ptrdiff_t)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (ptrdiff_t))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (ptrdiff_t)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (ptrdiff_t)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_ptrdiff_t=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (ptrdiff_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (ptrdiff_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_ptrdiff_t=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_ptrdiff_t" >&5 +echo "${ECHO_T}$ac_cv_sizeof_ptrdiff_t" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_PTRDIFF_T $ac_cv_sizeof_ptrdiff_t +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized ptrdiff_t" >&5 +echo $ECHO_N "checking for uniquely sized ptrdiff_t... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_ptrdiff_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_ptrdiff_t)" >/dev/null ; then + ccvs_cv_unique_int_type_ptrdiff_t=no + else + ccvs_cv_unique_int_type_ptrdiff_t=yes\($ac_cv_sizeof_ptrdiff_t\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_ptrdiff_t" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_ptrdiff_t" >&6 + if test $ccvs_cv_unique_int_type_ptrdiff_t != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_PTRDIFF_T 1 +_ACEOF + + fi + if test $gt_cv_c_wint_t != no; then + echo "$as_me:$LINENO: checking for wint_t" >&5 +echo $ECHO_N "checking for wint_t... $ECHO_C" >&6 +if test "${ac_cv_type_wint_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + + +int +main () +{ +if ((wint_t *) 0) + return 0; +if (sizeof (wint_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_wint_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_wint_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_wint_t" >&5 +echo "${ECHO_T}$ac_cv_type_wint_t" >&6 + +echo "$as_me:$LINENO: checking size of wint_t" >&5 +echo $ECHO_N "checking size of wint_t... $ECHO_C" >&6 +if test "${ac_cv_sizeof_wint_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_wint_t" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (wint_t))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (wint_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (wint_t))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (wint_t))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (wint_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_wint_t=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (wint_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (wint_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + + +long longval () { return (long) (sizeof (wint_t)); } +unsigned long ulongval () { return (long) (sizeof (wint_t)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (wint_t))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (wint_t)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (wint_t)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_wint_t=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (wint_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (wint_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_wint_t=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_wint_t" >&5 +echo "${ECHO_T}$ac_cv_sizeof_wint_t" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_WINT_T $ac_cv_sizeof_wint_t +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized wint_t" >&5 +echo $ECHO_N "checking for uniquely sized wint_t... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_wint_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_wint_t)" >/dev/null ; then + ccvs_cv_unique_int_type_wint_t=no + else + ccvs_cv_unique_int_type_wint_t=yes\($ac_cv_sizeof_wint_t\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_wint_t" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_wint_t" >&6 + if test $ccvs_cv_unique_int_type_wint_t != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_WINT_T 1 +_ACEOF + + fi + fi + if test $gt_cv_c_intmax_t != no; then + echo "$as_me:$LINENO: checking for intmax_t" >&5 +echo $ECHO_N "checking for intmax_t... $ECHO_C" >&6 +if test "${ac_cv_type_intmax_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#endif +#endif + + +int +main () +{ +if ((intmax_t *) 0) + return 0; +if (sizeof (intmax_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_intmax_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_intmax_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_intmax_t" >&5 +echo "${ECHO_T}$ac_cv_type_intmax_t" >&6 + +echo "$as_me:$LINENO: checking size of intmax_t" >&5 +echo $ECHO_N "checking size of intmax_t... $ECHO_C" >&6 +if test "${ac_cv_sizeof_intmax_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_intmax_t" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#endif +#endif + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (intmax_t))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#endif +#endif + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (intmax_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#endif +#endif + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (intmax_t))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#endif +#endif + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (intmax_t))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#endif +#endif + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (intmax_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_intmax_t=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (intmax_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (intmax_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#endif +#endif + + +long longval () { return (long) (sizeof (intmax_t)); } +unsigned long ulongval () { return (long) (sizeof (intmax_t)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (intmax_t))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (intmax_t)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (intmax_t)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_intmax_t=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (intmax_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (intmax_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_intmax_t=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_intmax_t" >&5 +echo "${ECHO_T}$ac_cv_sizeof_intmax_t" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INTMAX_T $ac_cv_sizeof_intmax_t +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized intmax_t" >&5 +echo $ECHO_N "checking for uniquely sized intmax_t... $ECHO_C" >&6 +if test "${ccvs_cv_unique_int_type_intmax_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_int_type_ \ + |grep "($ac_cv_sizeof_intmax_t)" >/dev/null ; then + ccvs_cv_unique_int_type_intmax_t=no + else + ccvs_cv_unique_int_type_intmax_t=yes\($ac_cv_sizeof_intmax_t\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_int_type_intmax_t" >&5 +echo "${ECHO_T}$ccvs_cv_unique_int_type_intmax_t" >&6 + if test $ccvs_cv_unique_int_type_intmax_t != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_INT_TYPE_INTMAX_T 1 +_ACEOF + + fi + fi + + echo "$as_me:$LINENO: checking for float" >&5 +echo $ECHO_N "checking for float... $ECHO_C" >&6 +if test "${ac_cv_type_float+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((float *) 0) + return 0; +if (sizeof (float)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_float=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_float=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_float" >&5 +echo "${ECHO_T}$ac_cv_type_float" >&6 + +echo "$as_me:$LINENO: checking size of float" >&5 +echo $ECHO_N "checking size of float... $ECHO_C" >&6 +if test "${ac_cv_sizeof_float+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_float" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (float))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (float))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (float))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (float))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (float))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_float=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (float), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (float), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (float)); } +unsigned long ulongval () { return (long) (sizeof (float)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (float))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (float)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (float)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_float=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (float), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (float), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_float=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_float" >&5 +echo "${ECHO_T}$ac_cv_sizeof_float" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_FLOAT $ac_cv_sizeof_float +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized float" >&5 +echo $ECHO_N "checking for uniquely sized float... $ECHO_C" >&6 +if test "${ccvs_cv_unique_float_type_float+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_float_type_ \ + |grep "($ac_cv_sizeof_float)" >/dev/null ; then + ccvs_cv_unique_float_type_float=no + else + ccvs_cv_unique_float_type_float=yes\($ac_cv_sizeof_float\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_float_type_float" >&5 +echo "${ECHO_T}$ccvs_cv_unique_float_type_float" >&6 + if test $ccvs_cv_unique_float_type_float != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_FLOAT_TYPE_FLOAT 1 +_ACEOF + + fi + echo "$as_me:$LINENO: checking for double" >&5 +echo $ECHO_N "checking for double... $ECHO_C" >&6 +if test "${ac_cv_type_double+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((double *) 0) + return 0; +if (sizeof (double)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_double=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_double=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_double" >&5 +echo "${ECHO_T}$ac_cv_type_double" >&6 + +echo "$as_me:$LINENO: checking size of double" >&5 +echo $ECHO_N "checking size of double... $ECHO_C" >&6 +if test "${ac_cv_sizeof_double+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_double" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (double))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (double))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (double))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (double))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (double))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_double=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (double), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (double), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (double)); } +unsigned long ulongval () { return (long) (sizeof (double)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (double))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (double)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (double)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_double=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (double), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (double), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_double=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_double" >&5 +echo "${ECHO_T}$ac_cv_sizeof_double" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_DOUBLE $ac_cv_sizeof_double +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized double" >&5 +echo $ECHO_N "checking for uniquely sized double... $ECHO_C" >&6 +if test "${ccvs_cv_unique_float_type_double+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_float_type_ \ + |grep "($ac_cv_sizeof_double)" >/dev/null ; then + ccvs_cv_unique_float_type_double=no + else + ccvs_cv_unique_float_type_double=yes\($ac_cv_sizeof_double\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_float_type_double" >&5 +echo "${ECHO_T}$ccvs_cv_unique_float_type_double" >&6 + if test $ccvs_cv_unique_float_type_double != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_FLOAT_TYPE_DOUBLE 1 +_ACEOF + + fi + if test $gt_cv_c_long_double != no; then + echo "$as_me:$LINENO: checking for long double" >&5 +echo $ECHO_N "checking for long double... $ECHO_C" >&6 +if test "${ac_cv_type_long_double+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((long double *) 0) + return 0; +if (sizeof (long double)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long_double=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long_double=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long_double" >&5 +echo "${ECHO_T}$ac_cv_type_long_double" >&6 + +echo "$as_me:$LINENO: checking size of long double" >&5 +echo $ECHO_N "checking size of long double... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long_double+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long_double" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long double))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long double))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long double))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long double))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long double))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_long_double=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (long double), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long double), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (long double)); } +unsigned long ulongval () { return (long) (sizeof (long double)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (long double))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (long double)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (long double)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long_double=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (long double), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long double), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long_double=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_long_double" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long_double" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG_DOUBLE $ac_cv_sizeof_long_double +_ACEOF + + + echo "$as_me:$LINENO: checking for uniquely sized long double" >&5 +echo $ECHO_N "checking for uniquely sized long double... $ECHO_C" >&6 +if test "${ccvs_cv_unique_float_type_long_double+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if set |grep ^ccvs_cv_unique_float_type_ \ + |grep "($ac_cv_sizeof_long_double)" >/dev/null ; then + ccvs_cv_unique_float_type_long_double=no + else + ccvs_cv_unique_float_type_long_double=yes\($ac_cv_sizeof_long_double\) + fi +fi +echo "$as_me:$LINENO: result: $ccvs_cv_unique_float_type_long_double" >&5 +echo "${ECHO_T}$ccvs_cv_unique_float_type_long_double" >&6 + if test $ccvs_cv_unique_float_type_long_double != no ; then + +cat >>confdefs.h <<\_ACEOF +#define UNIQUE_FLOAT_TYPE_LONG_DOUBLE 1 +_ACEOF + + fi + fi +fi + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STRCHR 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MEMCHR 1 +_ACEOF + + + +cat >>confdefs.h <<\_ACEOF +#define REGEX_MALLOC 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _REGEX_RE_COMP 1 +_ACEOF + + + +for ac_header in unistd.h vfork.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in fork vfork +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + echo "$as_me:$LINENO: checking for working fork" >&5 +echo $ECHO_N "checking for working fork... $ECHO_C" >&6 +if test "${ac_cv_func_fork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* By Ruediger Kuhlmann. */ + #include + #if HAVE_UNISTD_H + # include + #endif + /* Some systems only have a dummy stub for fork() */ + int main () + { + if (fork() < 0) + exit (1); + exit (0); + } +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +echo "${ECHO_T}$ac_cv_func_fork_works" >&6 + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + echo "$as_me:$LINENO: checking for working vfork" >&5 +echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 +if test "${ac_cv_func_vfork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +# include +#endif +#if HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + exit( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether closedir returns void" >&5 +echo $ECHO_N "checking whether closedir returns void... $ECHO_C" >&6 +if test "${ac_cv_func_closedir_void+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_closedir_void=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header_dirent> +#ifndef __cplusplus +int closedir (); +#endif + +int +main () +{ +exit (closedir (opendir (".")) != 0); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_closedir_void=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_closedir_void=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_closedir_void" >&5 +echo "${ECHO_T}$ac_cv_func_closedir_void" >&6 +if test $ac_cv_func_closedir_void = yes; then + +cat >>confdefs.h <<\_ACEOF +#define CLOSEDIR_VOID 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for library containing getspnam" >&5 +echo $ECHO_N "checking for library containing getspnam... $ECHO_C" >&6 +if test "${ac_cv_search_getspnam+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_getspnam=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +getspnam (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_getspnam="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_getspnam" = no; then + for ac_lib in sec gen; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +getspnam (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_getspnam="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_getspnam" >&5 +echo "${ECHO_T}$ac_cv_search_getspnam" >&6 +if test "$ac_cv_search_getspnam" != no; then + test "$ac_cv_search_getspnam" = "none required" || LIBS="$ac_cv_search_getspnam $LIBS" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETSPNAM 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking whether utime accepts a null argument" >&5 +echo $ECHO_N "checking whether utime accepts a null argument... $ECHO_C" >&6 +if test "${ac_cv_func_utime_null+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.data; >conftest.data +# Sequent interprets utime(file, 0) to mean use start of epoch. Wrong. +if test "$cross_compiling" = yes; then + ac_cv_func_utime_null=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat s, t; + exit (!(stat ("conftest.data", &s) == 0 + && utime ("conftest.data", (long *)0) == 0 + && stat ("conftest.data", &t) == 0 + && t.st_mtime >= s.st_mtime + && t.st_mtime - s.st_mtime < 120)); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_utime_null=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_utime_null=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +rm -f core *.core +fi +echo "$as_me:$LINENO: result: $ac_cv_func_utime_null" >&5 +echo "${ECHO_T}$ac_cv_func_utime_null" >&6 +if test $ac_cv_func_utime_null = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_UTIME_NULL 1 +_ACEOF + +fi +rm -f conftest.data + +echo "$as_me:$LINENO: checking for long file names" >&5 +echo $ECHO_N "checking for long file names... $ECHO_C" >&6 +if test "${ac_cv_sys_long_file_names+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_sys_long_file_names=yes +# Test for long file names in all the places we know might matter: +# . the current directory, where building will happen +# $prefix/lib where we will be installing things +# $exec_prefix/lib likewise +# eval it to expand exec_prefix. +# $TMPDIR if set, where it might want to write temporary files +# if $TMPDIR is not set: +# /tmp where it might want to write temporary files +# /var/tmp likewise +# /usr/tmp likewise +if test -n "$TMPDIR" && test -d "$TMPDIR" && test -w "$TMPDIR"; then + ac_tmpdirs=$TMPDIR +else + ac_tmpdirs='/tmp /var/tmp /usr/tmp' +fi +for ac_dir in . $ac_tmpdirs `eval echo $prefix/lib $exec_prefix/lib` ; do + test -d $ac_dir || continue + test -w $ac_dir || continue # It is less confusing to not echo anything here. + ac_xdir=$ac_dir/cf$$ + (umask 077 && mkdir $ac_xdir 2>/dev/null) || continue + ac_tf1=$ac_xdir/conftest9012345 + ac_tf2=$ac_xdir/conftest9012346 + (echo 1 >$ac_tf1) 2>/dev/null + (echo 2 >$ac_tf2) 2>/dev/null + ac_val=`cat $ac_tf1 2>/dev/null` + if test ! -f $ac_tf1 || test "$ac_val" != 1; then + ac_cv_sys_long_file_names=no + rm -rf $ac_xdir 2>/dev/null + break + fi + rm -rf $ac_xdir 2>/dev/null +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_long_file_names" >&5 +echo "${ECHO_T}$ac_cv_sys_long_file_names" >&6 +if test $ac_cv_sys_long_file_names = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LONG_FILE_NAMES 1 +_ACEOF + +fi + + + + +for ac_header in stdlib.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in getpagesize +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking for working mmap" >&5 +echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 +if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_mmap_fixed_mapped=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +/* malloc might have been renamed as rpl_malloc. */ +#undef malloc + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the file system buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propagated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ + +#include +#include + +#if !STDC_HEADERS && !HAVE_STDLIB_H +char *malloc (); +#endif + +/* This mess was copied from the GNU getpagesize.h. */ +#if !HAVE_GETPAGESIZE +/* Assume that all systems that can run configure have sys/param.h. */ +# if !HAVE_SYS_PARAM_H +# define HAVE_SYS_PARAM_H 1 +# endif + +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# if HAVE_SYS_PARAM_H +# include +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +int +main () +{ + char *data, *data2, *data3; + int i, pagesize; + int fd; + + pagesize = getpagesize (); + + /* First, make a file with some known garbage in it. */ + data = (char *) malloc (pagesize); + if (!data) + exit (1); + for (i = 0; i < pagesize; ++i) + *(data + i) = rand (); + umask (0); + fd = creat ("conftest.mmap", 0600); + if (fd < 0) + exit (1); + if (write (fd, data, pagesize) != pagesize) + exit (1); + close (fd); + + /* Next, try to mmap the file at a fixed address which already has + something else allocated at it. If we can, also make sure that + we see the same garbage. */ + fd = open ("conftest.mmap", O_RDWR); + if (fd < 0) + exit (1); + data2 = (char *) malloc (2 * pagesize); + if (!data2) + exit (1); + data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); + if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + exit (1); + + /* Finally, make sure that changes to the mapped area do not + percolate back to the file as seen by read(). (This is a bug on + some variants of i386 svr4.0.) */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = (char *) malloc (pagesize); + if (!data3) + exit (1); + if (read (fd, data3, pagesize) != pagesize) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + exit (1); + close (fd); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_mmap_fixed_mapped=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_mmap_fixed_mapped=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 +echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 +if test $ac_cv_func_mmap_fixed_mapped = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MMAP 1 +_ACEOF + +fi +rm -f conftest.mmap + + +echo "$as_me:$LINENO: checking whether printf supports %p" >&5 +echo $ECHO_N "checking whether printf supports %p... $ECHO_C" >&6 +if test "${cvs_cv_func_printf_ptr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +/* If printf supports %p, exit 0. */ +int +main () +{ + void *p1, *p2; + char buf[256]; + p1 = &p1; p2 = &p2; + sprintf(buf, "%p", p1); + exit(sscanf(buf, "%p", &p2) != 1 || p2 != p1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cvs_cv_func_printf_ptr=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +cvs_cv_func_printf_ptr=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +rm -f core core.* *.core +fi +echo "$as_me:$LINENO: result: $cvs_cv_func_printf_ptr" >&5 +echo "${ECHO_T}$cvs_cv_func_printf_ptr" >&6 +if test $cvs_cv_func_printf_ptr = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PRINTF_PTR 1 +_ACEOF + +fi + + +# Try to find connect and gethostbyname. + +echo "$as_me:$LINENO: checking for main in -lnsl" >&5 +echo $ECHO_N "checking for main in -lnsl... $ECHO_C" >&6 +if test "${ac_cv_lib_nsl_main+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + +int +main () +{ +main (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_nsl_main=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_nsl_main=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_main" >&5 +echo "${ECHO_T}$ac_cv_lib_nsl_main" >&6 +if test $ac_cv_lib_nsl_main = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBNSL 1 +_ACEOF + + LIBS="-lnsl $LIBS" + +fi + +echo "$as_me:$LINENO: checking for library containing connect" >&5 +echo $ECHO_N "checking for library containing connect... $ECHO_C" >&6 +if test "${ac_cv_search_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_connect=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_connect="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_connect" = no; then + for ac_lib in xnet socket inet; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_connect="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_connect" >&5 +echo "${ECHO_T}$ac_cv_search_connect" >&6 +if test "$ac_cv_search_connect" != no; then + test "$ac_cv_search_connect" = "none required" || LIBS="$ac_cv_search_connect $LIBS" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CONNECT 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for library containing gethostbyname" >&5 +echo $ECHO_N "checking for library containing gethostbyname... $ECHO_C" >&6 +if test "${ac_cv_search_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_gethostbyname=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_gethostbyname="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_gethostbyname" = no; then + for ac_lib in netinet; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_gethostbyname="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_search_gethostbyname" >&6 +if test "$ac_cv_search_gethostbyname" != no; then + test "$ac_cv_search_gethostbyname" = "none required" || LIBS="$ac_cv_search_gethostbyname $LIBS" + +fi + + + + + + + +KRB4=/usr/kerberos + + +# Check whether --with-krb4 or --without-krb4 was given. +if test "${with_krb4+set}" = set; then + withval="$with_krb4" + KRB4=$with_krb4 +fi; echo "$as_me:$LINENO: checking for KRB4 in $KRB4" >&5 +echo $ECHO_N "checking for KRB4 in $KRB4... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: " >&5 +echo "${ECHO_T}" >&6 + + +krb_h= +echo "$as_me:$LINENO: checking for krb.h" >&5 +echo $ECHO_N "checking for krb.h... $ECHO_C" >&6 +if test "$cross_compiling" != yes && test -r $KRB4/include/krb.h; then + hold_cflags=$CFLAGS + CFLAGS="$CFLAGS -I$KRB4/include" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + krb_h=yes krb_incdir=$KRB4/include +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +CFLAGS=$hold_cflags + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + krb_h=yes krb_incdir= +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$hold_cflags +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + krb_h=yes krb_incdir= +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +if test -z "$krb_h"; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + krb_h=yes krb_incdir= +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +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 <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + krb_h=yes krb_incdir=$KRB4/include/kerberosIV +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$hold_cflags + fi +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $krb_h" >&5 +echo "${ECHO_T}$krb_h" >&6 + +if test -n "$krb_h"; then + krb_lib= + if test "$cross_compiling" != yes && test -r $KRB4/lib/libkrb.a; then + hold_ldflags=$LDFLAGS + LDFLAGS="-L${KRB4}/lib $LDFLAGS" + echo "$as_me:$LINENO: checking for printf in -lkrb" >&5 +echo $ECHO_N "checking for printf in -lkrb... $ECHO_C" >&6 +if test "${ac_cv_lib_krb_printf+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lkrb $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +printf (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_krb_printf=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_krb_printf=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_krb_printf" >&5 +echo "${ECHO_T}$ac_cv_lib_krb_printf" >&6 +if test $ac_cv_lib_krb_printf = yes; then + krb_lib=yes krb_libdir=${KRB4}/lib +else + LDFLAGS=$hold_ldflags + # Using open here instead of printf so we don't + # get confused by the cached value for printf from above. + echo "$as_me:$LINENO: checking for open in -lkrb" >&5 +echo $ECHO_N "checking for open in -lkrb... $ECHO_C" >&6 +if test "${ac_cv_lib_krb_open+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lkrb $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char open (); +int +main () +{ +open (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_krb_open=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_krb_open=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_krb_open" >&5 +echo "${ECHO_T}$ac_cv_lib_krb_open" >&6 +if test $ac_cv_lib_krb_open = yes; then + krb_lib=yes krb_libdir= +fi + +fi + + LDFLAGS=$hold_ldflags + else + echo "$as_me:$LINENO: checking for printf in -lkrb" >&5 +echo $ECHO_N "checking for printf in -lkrb... $ECHO_C" >&6 +if test "${ac_cv_lib_krb_printf+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lkrb $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +printf (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_krb_printf=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_krb_printf=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_krb_printf" >&5 +echo "${ECHO_T}$ac_cv_lib_krb_printf" >&6 +if test $ac_cv_lib_krb_printf = yes; then + krb_lib=yes krb_libdir= +fi + + echo "$as_me:$LINENO: checking for krb_recvauth" >&5 +echo $ECHO_N "checking for krb_recvauth... $ECHO_C" >&6 +if test "${ac_cv_func_krb_recvauth+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define krb_recvauth to an innocuous variant, in case declares krb_recvauth. + For example, HP-UX 11i declares gettimeofday. */ +#define krb_recvauth innocuous_krb_recvauth + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char krb_recvauth (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef krb_recvauth + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char krb_recvauth (); +/* 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_krb_recvauth) || defined (__stub___krb_recvauth) +choke me +#else +char (*f) () = krb_recvauth; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != krb_recvauth; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_krb_recvauth=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_krb_recvauth=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_krb_recvauth" >&5 +echo "${ECHO_T}$ac_cv_func_krb_recvauth" >&6 +if test $ac_cv_func_krb_recvauth = yes; then + krb_lib=yes krb_libdir= +fi + + fi + if test -n "$krb_lib"; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_KERBEROS 1 +_ACEOF + + cvs_client_objects="$cvs_client_objects kerberos4-client.o" + test -n "${krb_libdir}" && LIBS="${LIBS} -L${krb_libdir}" + # 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 "$as_me:$LINENO: checking for printf in -ldes" >&5 +echo $ECHO_N "checking for printf in -ldes... $ECHO_C" >&6 +if test "${ac_cv_lib_des_printf+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldes $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +printf (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_des_printf=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_des_printf=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_des_printf" >&5 +echo "${ECHO_T}$ac_cv_lib_des_printf" >&6 +if test $ac_cv_lib_des_printf = yes; then + LIBS="${LIBS} -ldes" +fi + + +echo "$as_me:$LINENO: checking for krb_recvauth in -lkrb" >&5 +echo $ECHO_N "checking for krb_recvauth in -lkrb... $ECHO_C" >&6 +if test "${ac_cv_lib_krb_krb_recvauth+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lkrb $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char krb_recvauth (); +int +main () +{ +krb_recvauth (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_krb_krb_recvauth=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_krb_krb_recvauth=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_krb_krb_recvauth" >&5 +echo "${ECHO_T}$ac_cv_lib_krb_krb_recvauth" >&6 +if test $ac_cv_lib_krb_krb_recvauth = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBKRB 1 +_ACEOF + + LIBS="-lkrb $LIBS" + +fi + + +echo "$as_me:$LINENO: checking for krb_recvauth in -lkrb4" >&5 +echo $ECHO_N "checking for krb_recvauth in -lkrb4... $ECHO_C" >&6 +if test "${ac_cv_lib_krb4_krb_recvauth+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lkrb4 $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char krb_recvauth (); +int +main () +{ +krb_recvauth (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_krb4_krb_recvauth=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_krb4_krb_recvauth=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_krb4_krb_recvauth" >&5 +echo "${ECHO_T}$ac_cv_lib_krb4_krb_recvauth" >&6 +if test $ac_cv_lib_krb4_krb_recvauth = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBKRB4 1 +_ACEOF + + LIBS="-lkrb4 $LIBS" + +fi + + LDFLAGS=$hold_ldflags + if test -n "$krb_incdir"; then + CPPFLAGS="$CPPFLAGS -I$krb_incdir" + fi + fi +fi + +for ac_func in krb_get_err_text +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + + +# +# Use --with-gssapi[=DIR] to enable GSSAPI support. +# +# defaults to enabled with DIR in default list below +# +# Search for /SUNHEA/ and read the comments about this default below. +# + +# Check whether --with-gssapi or --without-gssapi was given. +if test "${with_gssapi+set}" = set; then + withval="$with_gssapi" + +else + with_gssapi=yes +fi; +# +# Try to locate a GSSAPI installation if no location was specified, assuming +# GSSAPI was enabled (the default). +# +if test -n "$acx_gssapi_cv_gssapi"; then + # Granted, this is a slightly ugly way to print this info, but the + # AC_CHECK_HEADER used in the search for a GSSAPI installation makes using + # AC_CACHE_CHECK worse + echo "$as_me:$LINENO: checking for GSSAPI" >&5 +echo $ECHO_N "checking for GSSAPI... $ECHO_C" >&6 +else :; fi +if test "${acx_gssapi_cv_gssapi+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + +if test x$with_gssapi = xyes; then + # --with but no location specified + # assume a gssapi.h or gssapi/gssapi.h locates our install. + # + # This isn't always strictly true. For instance Solaris 7's SUNHEA (header) + # package installs gssapi.h whether or not the necessary libraries are + # installed. I'm still not sure whether to consider this a bug. The long + # way around is to not consider GSSPAI installed unless gss_import_name is + # found, but that brings up a lot of other hassles, like continuing to let + # gcc & ld generate the error messages when the user uses --with-gssapi=dir + # as a debugging aid. The short way around is to disable GSSAPI by default, + # but I think Sun users have been faced with this for awhile and I haven't + # heard many complaints. + acx_gssapi_save_CPPFLAGS=$CPPFLAGS + for acx_gssapi_cv_gssapi in yes /usr/kerberos /usr/cygnus/kerbnet no; do + if test x$acx_gssapi_cv_gssapi = xno; then + break + fi + if test x$acx_gssapi_cv_gssapi = xyes; then + echo "$as_me:$LINENO: checking for GSSAPI" >&5 +echo $ECHO_N "checking for GSSAPI... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: " >&5 +echo "${ECHO_T}" >&6 + else + CPPFLAGS="$acx_gssapi_save_CPPFLAGS -I$acx_gssapi_cv_gssapi/include" + echo "$as_me:$LINENO: checking for GSSAPI in $acx_gssapi_cv_gssapi" >&5 +echo $ECHO_N "checking for GSSAPI in $acx_gssapi_cv_gssapi... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: " >&5 +echo "${ECHO_T}" >&6 + fi + unset ac_cv_header_gssapi_h + unset ac_cv_header_gssapi_gssapi_h + unset ac_cv_header_krb5_h + + + +for ac_header in gssapi.h gssapi/gssapi.h krb5.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + if (test "$ac_cv_header_gssapi_h" = yes || + test "$ac_cv_header_gssapi_gssapi_h" = yes) && + test "$ac_cv_header_krb5_h" = yes; then + break + fi + done + CPPFLAGS=$acx_gssapi_save_CPPFLAGS +else + acx_gssapi_cv_gssapi=$with_gssapi +fi +echo "$as_me:$LINENO: checking for GSSAPI" >&5 +echo $ECHO_N "checking for GSSAPI... $ECHO_C" >&6 + +fi +echo "$as_me:$LINENO: result: $acx_gssapi_cv_gssapi" >&5 +echo "${ECHO_T}$acx_gssapi_cv_gssapi" >&6 + +# +# Set up GSSAPI includes for later use. We don't bother to check for +# $acx_gssapi_cv_gssapi=no here since that will be caught later. +# +if test x$acx_gssapi_cv_gssapi = xyes; then + # no special includes necessary + GSSAPI_INCLUDES="" +else + # GSSAPI at $acx_gssapi_cv_gssapi (could be 'no') + GSSAPI_INCLUDES=" -I$acx_gssapi_cv_gssapi/include" +fi + +# +# Get the rest of the information CVS needs to compile with GSSAPI support +# +if test x$acx_gssapi_cv_gssapi != xno; then + # define HAVE_GSSAPI and set up the includes + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GSSAPI +_ACEOF + + CPPFLAGS=$CPPFLAGS$GSSAPI_INCLUDES + + cvs_client_objects="$cvs_client_objects gssapi-client.o" + + # locate any other headers + + + + +for ac_header in gssapi.h gssapi/gssapi.h gssapi/gssapi_generic.h krb5.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + # And look through them for GSS_C_NT_HOSTBASED_SERVICE or its alternatives + echo "$as_me:$LINENO: checking for GSS_C_NT_HOSTBASED_SERVICE" >&5 +echo $ECHO_N "checking for GSS_C_NT_HOSTBASED_SERVICE... $ECHO_C" >&6 +if test "${acx_gssapi_cv_gss_c_nt_hostbased_service+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + acx_gssapi_cv_gss_c_nt_hostbased_service=no + if test "$ac_cv_header_gssapi_h" = "yes"; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "GSS_C_NT_HOSTBASED_SERVICE" >/dev/null 2>&1; then + acx_gssapi_cv_gss_c_nt_hostbased_service=yes +else + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "gss_nt_service_name" >/dev/null 2>&1; then + acx_gssapi_cv_gss_c_nt_hostbased_service=gss_nt_service_name +fi +rm -f conftest* + + +fi +rm -f conftest* + + fi + if test $acx_gssapi_cv_gss_c_nt_hostbased_service = no && + test "$ac_cv_header_gssapi_gssapi_h" = "yes"; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "GSS_C_NT_HOSTBASED_SERVICE" >/dev/null 2>&1; then + acx_gssapi_cv_gss_c_nt_hostbased_service=yes +else + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "gss_nt_service_name" >/dev/null 2>&1; then + acx_gssapi_cv_gss_c_nt_hostbased_service=gss_nt_service_name +fi +rm -f conftest* + + +fi +rm -f conftest* + + else :; fi + if test $acx_gssapi_cv_gss_c_nt_hostbased_service = no && + test "$ac_cv_header_gssapi_gssapi_generic_h" = "yes"; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "GSS_C_NT_HOSTBASED_SERVICE" >/dev/null 2>&1; then + acx_gssapi_cv_gss_c_nt_hostbased_service=yes +else + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "gss_nt_service_name" >/dev/null 2>&1; then + acx_gssapi_cv_gss_c_nt_hostbased_service=gss_nt_service_name +fi +rm -f conftest* + + +fi +rm -f conftest* + + else :; fi + +fi +echo "$as_me:$LINENO: result: $acx_gssapi_cv_gss_c_nt_hostbased_service" >&5 +echo "${ECHO_T}$acx_gssapi_cv_gss_c_nt_hostbased_service" >&6 + if test $acx_gssapi_cv_gss_c_nt_hostbased_service != yes && + test $acx_gssapi_cv_gss_c_nt_hostbased_service != no; then + # don't define for yes since that means it already means something and + # don't define for no since we'd rather the compiler catch the error + # It's debatable whether we'd prefer that the compiler catch the error + # - it seems our estranged developer is more likely to be familiar with + # the intricacies of the compiler than with those of autoconf, but by + # the same token, maybe we'd rather alert them to the fact that most + # of the support they need to fix the problem is installed if they can + # simply locate the appropriate symbol. + +cat >>confdefs.h <<_ACEOF +#define GSS_C_NT_HOSTBASED_SERVICE $acx_gssapi_cv_gss_c_nt_hostbased_service +_ACEOF + + else :; fi + + # Expect the libs to be installed parallel to the headers + # + # We could try once with and once without, but I'm not sure it's worth the + # trouble. + if test x$acx_gssapi_cv_gssapi != xyes; then + if test -z "$LIBS"; then + LIBS="-L$acx_gssapi_cv_gssapi/lib" + else + LIBS="-L$acx_gssapi_cv_gssapi/lib $LIBS" + fi + else :; fi + + # + # Some of the order below is particular due to library dependencies + # + + # + # des Heimdal K 0.3d, but Heimdal seems to be set up such + # that it could have been installed from elsewhere. + # + echo "$as_me:$LINENO: checking for library containing des_set_odd_parity" >&5 +echo $ECHO_N "checking for library containing des_set_odd_parity... $ECHO_C" >&6 +if test "${ac_cv_search_des_set_odd_parity+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_des_set_odd_parity=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char des_set_odd_parity (); +int +main () +{ +des_set_odd_parity (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_des_set_odd_parity="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_des_set_odd_parity" = no; then + for ac_lib in des; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char des_set_odd_parity (); +int +main () +{ +des_set_odd_parity (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_des_set_odd_parity="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_des_set_odd_parity" >&5 +echo "${ECHO_T}$ac_cv_search_des_set_odd_parity" >&6 +if test "$ac_cv_search_des_set_odd_parity" != no; then + test "$ac_cv_search_des_set_odd_parity" = "none required" || LIBS="$ac_cv_search_des_set_odd_parity $LIBS" + +fi + + + # + # com_err Heimdal K 0.3d + # + # com_err MIT K5 v1.2.2-beta1 + # + echo "$as_me:$LINENO: checking for library containing com_err" >&5 +echo $ECHO_N "checking for library containing com_err... $ECHO_C" >&6 +if test "${ac_cv_search_com_err+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_com_err=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char com_err (); +int +main () +{ +com_err (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_com_err="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_com_err" = no; then + for ac_lib in com_err; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char com_err (); +int +main () +{ +com_err (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_com_err="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_com_err" >&5 +echo "${ECHO_T}$ac_cv_search_com_err" >&6 +if test "$ac_cv_search_com_err" != no; then + test "$ac_cv_search_com_err" = "none required" || LIBS="$ac_cv_search_com_err $LIBS" + +fi + + + # + # asn1 Heimdal K 0.3d -lcom_err + # + echo "$as_me:$LINENO: checking for library containing initialize_asn1_error_table_r" >&5 +echo $ECHO_N "checking for library containing initialize_asn1_error_table_r... $ECHO_C" >&6 +if test "${ac_cv_search_initialize_asn1_error_table_r+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_initialize_asn1_error_table_r=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char initialize_asn1_error_table_r (); +int +main () +{ +initialize_asn1_error_table_r (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_initialize_asn1_error_table_r="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_initialize_asn1_error_table_r" = no; then + for ac_lib in asn1; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char initialize_asn1_error_table_r (); +int +main () +{ +initialize_asn1_error_table_r (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_initialize_asn1_error_table_r="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_initialize_asn1_error_table_r" >&5 +echo "${ECHO_T}$ac_cv_search_initialize_asn1_error_table_r" >&6 +if test "$ac_cv_search_initialize_asn1_error_table_r" != no; then + test "$ac_cv_search_initialize_asn1_error_table_r" = "none required" || LIBS="$ac_cv_search_initialize_asn1_error_table_r $LIBS" + +fi + + + # + # resolv required, but not installed by Heimdal K 0.3d + # + # resolv MIT K5 1.2.2-beta1 + # Linux 2.2.17 + # + echo "$as_me:$LINENO: checking for library containing __dn_expand" >&5 +echo $ECHO_N "checking for library containing __dn_expand... $ECHO_C" >&6 +if test "${ac_cv_search___dn_expand+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search___dn_expand=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char __dn_expand (); +int +main () +{ +__dn_expand (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search___dn_expand="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search___dn_expand" = no; then + for ac_lib in resolv; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char __dn_expand (); +int +main () +{ +__dn_expand (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search___dn_expand="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search___dn_expand" >&5 +echo "${ECHO_T}$ac_cv_search___dn_expand" >&6 +if test "$ac_cv_search___dn_expand" != no; then + test "$ac_cv_search___dn_expand" = "none required" || LIBS="$ac_cv_search___dn_expand $LIBS" + +fi + + + # + # crypt Needed by roken under FreeBSD 4.6. + # + echo "$as_me:$LINENO: checking for library containing crypt" >&5 +echo $ECHO_N "checking for library containing crypt... $ECHO_C" >&6 +if test "${ac_cv_search_crypt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_crypt=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +crypt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_crypt="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_crypt" = no; then + for ac_lib in crypt; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +crypt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_crypt="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_crypt" >&5 +echo "${ECHO_T}$ac_cv_search_crypt" >&6 +if test "$ac_cv_search_crypt" != no; then + test "$ac_cv_search_crypt" = "none required" || LIBS="$ac_cv_search_crypt $LIBS" + +fi + + + # + # roken Heimdal K 0.3d -lresolv + # roken FreeBSD 4.6 -lcrypt + # + echo "$as_me:$LINENO: checking for library containing roken_gethostbyaddr" >&5 +echo $ECHO_N "checking for library containing roken_gethostbyaddr... $ECHO_C" >&6 +if test "${ac_cv_search_roken_gethostbyaddr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_roken_gethostbyaddr=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char roken_gethostbyaddr (); +int +main () +{ +roken_gethostbyaddr (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_roken_gethostbyaddr="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_roken_gethostbyaddr" = no; then + for ac_lib in roken; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char roken_gethostbyaddr (); +int +main () +{ +roken_gethostbyaddr (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_roken_gethostbyaddr="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_roken_gethostbyaddr" >&5 +echo "${ECHO_T}$ac_cv_search_roken_gethostbyaddr" >&6 +if test "$ac_cv_search_roken_gethostbyaddr" != no; then + test "$ac_cv_search_roken_gethostbyaddr" = "none required" || LIBS="$ac_cv_search_roken_gethostbyaddr $LIBS" + +fi + + + # + # k5crypto MIT K5 v1.2.2-beta1 + # + echo "$as_me:$LINENO: checking for library containing valid_enctype" >&5 +echo $ECHO_N "checking for library containing valid_enctype... $ECHO_C" >&6 +if test "${ac_cv_search_valid_enctype+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_valid_enctype=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char valid_enctype (); +int +main () +{ +valid_enctype (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_valid_enctype="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_valid_enctype" = no; then + for ac_lib in k5crypto; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char valid_enctype (); +int +main () +{ +valid_enctype (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_valid_enctype="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_valid_enctype" >&5 +echo "${ECHO_T}$ac_cv_search_valid_enctype" >&6 +if test "$ac_cv_search_valid_enctype" != no; then + test "$ac_cv_search_valid_enctype" = "none required" || LIBS="$ac_cv_search_valid_enctype $LIBS" + +fi + + + # + # gen ? ? ? Needed on Irix 5.3 with some + # Irix 5.3 version of Kerberos. I'm not + # sure which since Irix didn't + # get any testing this time + # around. Original comment: + # + # This is necessary on Irix 5.3, in order to link against libkrb5 -- + # there, an_to_ln.o refers to things defined only in -lgen. + # + echo "$as_me:$LINENO: checking for library containing compile" >&5 +echo $ECHO_N "checking for library containing compile... $ECHO_C" >&6 +if test "${ac_cv_search_compile+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_compile=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char compile (); +int +main () +{ +compile (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_compile="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_compile" = no; then + for ac_lib in gen; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char compile (); +int +main () +{ +compile (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_compile="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_compile" >&5 +echo "${ECHO_T}$ac_cv_search_compile" >&6 +if test "$ac_cv_search_compile" != no; then + test "$ac_cv_search_compile" = "none required" || LIBS="$ac_cv_search_compile $LIBS" + +fi + + + # + # krb5 ? ? ? -lgen -l??? + # Irix 5.3 + # + # krb5 MIT K5 v1.1.1 + # + # krb5 MIT K5 v1.2.2-beta1 -lcrypto -lcom_err + # Linux 2.2.17 + # + # krb5 MIT K5 v1.2.2-beta1 -lcrypto -lcom_err -lresolv + # + # krb5 Heimdal K 0.3d -lasn1 -lroken -ldes + # + echo "$as_me:$LINENO: checking for library containing krb5_free_context" >&5 +echo $ECHO_N "checking for library containing krb5_free_context... $ECHO_C" >&6 +if test "${ac_cv_search_krb5_free_context+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_krb5_free_context=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char krb5_free_context (); +int +main () +{ +krb5_free_context (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_krb5_free_context="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_krb5_free_context" = no; then + for ac_lib in krb5; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char krb5_free_context (); +int +main () +{ +krb5_free_context (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_krb5_free_context="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_krb5_free_context" >&5 +echo "${ECHO_T}$ac_cv_search_krb5_free_context" >&6 +if test "$ac_cv_search_krb5_free_context" != no; then + test "$ac_cv_search_krb5_free_context" = "none required" || LIBS="$ac_cv_search_krb5_free_context $LIBS" + +fi + + + # + # gssapi_krb5 Only lib needed with MIT K5 v1.2.1, so find it first in + # order to prefer MIT Kerberos. If both MIT & Heimdal + # Kerberos are installed and in the path, this will leave + # some of the libraries above in LIBS unnecessarily, but + # noone would ever do that, right? + # + # gssapi_krb5 MIT K5 v1.2.2-beta1 -lkrb5 + # + # gssapi Heimdal K 0.3d -lkrb5 + # + echo "$as_me:$LINENO: checking for library containing gss_import_name" >&5 +echo $ECHO_N "checking for library containing gss_import_name... $ECHO_C" >&6 +if test "${ac_cv_search_gss_import_name+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_gss_import_name=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gss_import_name (); +int +main () +{ +gss_import_name (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_gss_import_name="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_gss_import_name" = no; then + for ac_lib in gssapi_krb5 gssapi; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gss_import_name (); +int +main () +{ +gss_import_name (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_gss_import_name="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_gss_import_name" >&5 +echo "${ECHO_T}$ac_cv_search_gss_import_name" >&6 +if test "$ac_cv_search_gss_import_name" != no; then + test "$ac_cv_search_gss_import_name" = "none required" || LIBS="$ac_cv_search_gss_import_name $LIBS" + +fi + +fi + + + +# +# Use --with-zlib to build with a zlib other than the version distributed +# with CVS. +# +# defaults to the included snapshot of zlib +# + +# Check whether --with-external-zlib or --without-external-zlib was given. +if test "${with_external_zlib+set}" = set; then + withval="$with_external_zlib" + with_external_zlib=$withval +else + with_external_zlib=no +fi; + +# +# Try to locate a ZLIB installation if no location was specified, assuming +# external ZLIB was enabled. +# +if test -n "$acx_zlib_cv_external_zlib"; then + # Granted, this is a slightly ugly way to print this info, but the + # AC_CHECK_HEADER used in the search for a ZLIB installation makes using + # AC_CACHE_CHECK worse + echo "$as_me:$LINENO: checking for external ZLIB" >&5 +echo $ECHO_N "checking for external ZLIB... $ECHO_C" >&6 +else :; fi +if test "${acx_zlib_cv_external_zlib+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + # + # --with but no location specified + # assume zlib.h locates our install. + # + acx_zlib_save_CPPFLAGS=$CPPFLAGS + for acx_zlib_cv_external_zlib in yes /usr/local no; do + if test x$acx_zlib_cv_external_zlib = xno; then + break + fi + if test x$acx_zlib_cv_external_zlib = xyes; then + echo "$as_me:$LINENO: checking for external ZLIB" >&5 +echo $ECHO_N "checking for external ZLIB... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: " >&5 +echo "${ECHO_T}" >&6 + else + CPPFLAGS="$acx_zlib_save_CPPFLAGS -I$acx_zlib_cv_external_zlib/include" + echo "$as_me:$LINENO: checking for external ZLIB in $acx_zlib_cv_external_zlib" >&5 +echo $ECHO_N "checking for external ZLIB in $acx_zlib_cv_external_zlib... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: " >&5 +echo "${ECHO_T}" >&6 + fi + unset ac_cv_header_zlib_h + +for ac_header in zlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + if test "$ac_cv_header_zlib_h" = yes; then + break + fi + done + CPPFLAGS=$acx_zlib_save_CPPFLAGS +echo "$as_me:$LINENO: checking for external ZLIB" >&5 +echo $ECHO_N "checking for external ZLIB... $ECHO_C" >&6 + +fi +echo "$as_me:$LINENO: result: $acx_zlib_cv_external_zlib" >&5 +echo "${ECHO_T}$acx_zlib_cv_external_zlib" >&6 + + +# +# Output a pretty message naming our selected ZLIB "external" or "package" +# so that any warnings printed by the version check make more sense. +# +echo "$as_me:$LINENO: checking selected ZLIB" >&5 +echo $ECHO_N "checking selected ZLIB... $ECHO_C" >&6 +if test "x$with_external_zlib" = xno; then + echo "$as_me:$LINENO: result: package" >&5 +echo "${ECHO_T}package" >&6 +else + echo "$as_me:$LINENO: result: external" >&5 +echo "${ECHO_T}external" >&6 +fi + + +# +# Verify that the ZLIB we aren't using isn't newer than the one we are. +# +if test "x$acx_zlib_cv_external_zlib" != xno; then + LOCAL_ZLIB_VERSION=`sed -n '/^#define ZLIB_VERSION ".*"$/{ + s/^#define ZLIB_VERSION "\(.*\)"$/\1/; + p;}' <$srcdir/zlib/zlib.h 2>&5` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +ac_extract_cpp_result=`(eval "$ac_cpp -dM conftest.$ac_ext") 2>&5 | + sed -n "/^#define ZLIB_VERSION /{ + s/^#define ZLIB_VERSION //; + s/^ *//; + s/ *\$//; + p;}" 2>&5` +if test -n "$ac_extract_cpp_result"; then + ZLIB_VERSION=$ac_extract_cpp_result +fi +rm -f conftest* + + ZLIB_VERSION=`echo "$ZLIB_VERSION" |sed 's/"//g'` + if test "x$LOCAL_ZLIB_VERSION" = "x$ZLIB_VERSION"; then + # the strings are equal. run ACTION-IF-EQUAL and bail + : +else + # first unletter the versions + # this only works for a single trailing letter + asx_version_1=`echo "$LOCAL_ZLIB_VERSION" | + sed 's/\(abcedfghi\)/.\1/; + s/\(jklmnopqrs\)/.1\1/; + s/\(tuvwxyz\)/.2\1/; + y/abcdefghijklmnopqrstuvwxyz/12345678901234567890123456/;'` + asx_version_2=`echo "$ZLIB_VERSION" | + sed 's/\(abcedfghi\)/.\1/; + s/\(jklmnopqrs\)/.1\1/; + s/\(tuvwxyz\)/.2\1/; + y/abcdefghijklmnopqrstuvwxyz/12345678901234567890123456/;'` + asx_count=1 + asx_save_IFS=$IFS + IFS=. + asx_retval=-1 + for vsub1 in $asx_version_1; do + vsub2=`echo "$asx_version_2" |awk -F. "{print \\\$$asx_count}"` + if test -z "$vsub2" || test $vsub1 -gt $vsub2; then + asx_retval=1 + break + elif test $vsub1 -lt $vsub2; then + break + fi + asx_count=`expr $asx_count + 1` + done + IFS=$asx_save_IFS + if test $asx_retval -eq -1; then + if test "x$with_external_zlib" = xno; then + { echo "$as_me:$LINENO: WARNING: Found external ZLIB with a more recent version than the + package version ($ZLIB_VERSION > $LOCAL_ZLIB_VERSION). configure with the + --with-external-zlib option to select the more recent version." >&5 +echo "$as_me: WARNING: Found external ZLIB with a more recent version than the + package version ($ZLIB_VERSION > $LOCAL_ZLIB_VERSION). configure with the + --with-external-zlib option to select the more recent version." >&2;} + fi + else +if test "x$with_external_zlib" != xno; then + { echo "$as_me:$LINENO: WARNING: Package ZLIB is more recent than requested external version + ($LOCAL_ZLIB_VERSION > $ZLIB_VERSION). configure with the --without-external-zlib + option to select the more recent version." >&5 +echo "$as_me: WARNING: Package ZLIB is more recent than requested external version + ($LOCAL_ZLIB_VERSION > $ZLIB_VERSION). configure with the --without-external-zlib + option to select the more recent version." >&2;} + fi + fi + +fi + +fi + + +# Now set with_external_zlib to our discovered value or the user specified +# value, as appropriate. +if test x$with_external_zlib = xyes; then + with_external_zlib=$acx_zlib_cv_external_zlib +fi +# $with_external_zlib could still be "no" + + +# +# Set up ZLIB includes for later use. +# +if test x$with_external_zlib != xyes \ + && test x$with_external_zlib != no; then + if test -z "$CPPFLAGS"; then + CPPFLAGS="-I$with_external_zlib/include" + else + CPPFLAGS="$CPPFLAGS -I$with_external_zlib/include" + fi + if test -z "$LDFLAGS"; then + LDFLAGS="-L$with_external_zlib/lib" + else + LDFLAGS="$LDFLAGS -L$with_external_zlib/lib" + fi +fi + +ZLIB_CPPFLAGS= +ZLIB_LIBS= +ZLIB_SUBDIRS= +if test x$with_external_zlib = xno; then + # We need ZLIB_CPPFLAGS so that later executions of cpp from configure + # don't try to interpret $(top_srcdir) + ZLIB_CPPFLAGS='-I$(top_srcdir)/zlib' + ZLIB_LIBS='$(top_builddir)/zlib/libz.a' + # ZLIB_SUBDIRS is only used in the top level Makefiles. + ZLIB_SUBDIRS=zlib +else + # We know what to do now, so set up the CPPFLAGS, LDFLAGS, and LIBS for later + # use. + if test -z "$LIBS"; then + LIBS=-lz + else + LIBS="$LIBS -lz" + fi + + # + # Verify external installed zlib works + # + # Ideally, we would also check that the version is newer + # + echo "$as_me:$LINENO: checking that ZLIB library works" >&5 +echo $ECHO_N "checking that ZLIB library works... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +int i = Z_OK; const char *version = zlibVersion(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + { { echo "$as_me:$LINENO: error: ZLIB failed to link" >&5 +echo "$as_me: error: ZLIB failed to link" >&2;} + { (exit 1); exit 1; }; } +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + + + + +# What remote shell transport should our client cvs default to using? + +# Check whether --with-rsh or --without-rsh was given. +if test "${with_rsh+set}" = set; then + withval="$with_rsh" + +else + with_rsh="rsh ssh" +fi; + +if test no = "$with_rsh"; then + { echo "$as_me:$LINENO: WARNING: Failed to find usable remote shell. Using 'rsh'." >&5 +echo "$as_me: WARNING: Failed to find usable remote shell. Using 'rsh'." >&2;} + with_rsh=rsh +elif test yes = "$with_rsh"; then + # Make --with-rsh mean the same thing as --with-rsh=rsh + with_rsh=rsh +fi + +if echo $with_rsh |grep ^/ >/dev/null; then + # If $with_rsh is an absolute path, issue a warning if the executable + # doesn't exist or isn't usable, but then trust the user and use it + # regardless + with_default_rsh=$with_rsh + echo "$as_me:$LINENO: checking for a remote shell" >&5 +echo $ECHO_N "checking for a remote shell... $ECHO_C" >&6 + if ! test -f $with_rsh \ + || ! test -x $with_rsh; then + # warn the user that they may encounter problems + { echo "$as_me:$LINENO: WARNING: $with_rsh is not a path to an executable file" >&5 +echo "$as_me: WARNING: $with_rsh is not a path to an executable file" >&2;} + fi +else + # Search for a remote shell + for ac_prog in $with_rsh +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_with_default_rsh+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$with_default_rsh"; then + ac_cv_prog_with_default_rsh="$with_default_rsh" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_with_default_rsh="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +with_default_rsh=$ac_cv_prog_with_default_rsh +if test -n "$with_default_rsh"; then + echo "$as_me:$LINENO: result: $with_default_rsh" >&5 +echo "${ECHO_T}$with_default_rsh" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$with_default_rsh" && break +done +test -n "$with_default_rsh" || with_default_rsh=""rsh"" + +fi + + +cat >>confdefs.h <<_ACEOF +#define RSH_DFLT "$with_default_rsh" +_ACEOF + +RSH_DFLT=$with_default_rsh + + + + + +# Let the confiscator request a specific editor + +# Check whether --with-editor or --without-editor was given. +if test "${with_editor+set}" = set; then + withval="$with_editor" + +else + with_editor=yes +fi; + +# If --with-editor was supplied with an argument, let it override $EDITOR from +# the user's environment. We need to unset EDITOR here because AC_CHECK_PROGS +# will let the value of EDITOR ride when it is set rather than searching. We +# ignore the --without-editor case since it will be caught below. +if test -n "$EDITOR" && test yes != $with_editor; then + $as_unset EDITOR || test "${EDITOR+set}" != set || { EDITOR=; export EDITOR; } +fi + +# Set the default when --with-editor wasn't supplied or when it was supplied +# without an argument. +if test yes = $with_editor; then + with_editor="vim vi emacs nano pico edit" +fi + +if echo $with_editor |grep ^/ >/dev/null; then + # If $with_editor is an absolute path, issue a warning if the executable + # doesn't exist or isn't usable, but then trust the user and use it + # regardless + EDITOR=$with_editor + echo "$as_me:$LINENO: checking for an editor" >&5 +echo $ECHO_N "checking for an editor... $ECHO_C" >&6 + echo "$as_me:$LINENO: result: $EDITOR" >&5 +echo "${ECHO_T}$EDITOR" >&6 + if ! test -f $with_editor \ + || ! test -x $with_editor; then + # warn the user that they may encounter problems + { echo "$as_me:$LINENO: WARNING: \`$with_editor' is not a path to an executable file" >&5 +echo "$as_me: WARNING: \`$with_editor' is not a path to an executable file" >&2;} + fi +elif test no != "${with_editor}"; then + # Search for an editor + for ac_prog in $with_editor +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_EDITOR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$EDITOR"; then + ac_cv_prog_EDITOR="$EDITOR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_EDITOR="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +EDITOR=$ac_cv_prog_EDITOR +if test -n "$EDITOR"; then + echo "$as_me:$LINENO: result: $EDITOR" >&5 +echo "${ECHO_T}$EDITOR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$EDITOR" && break +done +test -n "$EDITOR" || EDITOR="no" + + if test no = "${EDITOR}"; then + { { echo "$as_me:$LINENO: error: + Failed to find a text file editor. CVS cannot be compiled + without a default log message editor. Searched for + \`$with_editor'. Try \`configure --with-editor'." >&5 +echo "$as_me: error: + Failed to find a text file editor. CVS cannot be compiled + without a default log message editor. Searched for + \`$with_editor'. Try \`configure --with-editor'." >&2;} + { (exit 1); exit 1; }; } + fi +else + { { echo "$as_me:$LINENO: error: + CVS cannot be compiled without a default log message editor. + Try \`configure --with-editor'." >&5 +echo "$as_me: error: + CVS cannot be compiled without a default log message editor. + Try \`configure --with-editor'." >&2;} + { (exit 1); exit 1; }; } +fi + + +cat >>confdefs.h <<_ACEOF +#define EDITOR_DFLT "$EDITOR" +_ACEOF + + + + + + +# Check whether --with-hardcoded-pam-service-name or --without-hardcoded-pam-service-name was given. +if test "${with_hardcoded_pam_service_name+set}" = set; then + withval="$with_hardcoded_pam_service_name" + +else + with_hardcoded_pam_service_name=cvs +fi; + +if test "x$with_hardcoded_pam_service_name" = xno || + test "x$with_hardcoded_pam_service_name" = xprogram_name; then + +cat >>confdefs.h <<\_ACEOF +#define PAM_SERVICE_NAME program_name +_ACEOF + +else + if test x"$with_hardcoded_pam_service_name" = xyes; then + with_hardcoded_pam_service_name=cvs + fi + cat >>confdefs.h <<_ACEOF +#define PAM_SERVICE_NAME "$with_hardcoded_pam_service_name" +_ACEOF + +fi + + + + +# Check whether --with-tmpdir or --without-tmpdir was given. +if test "${with_tmpdir+set}" = set; then + withval="$with_tmpdir" + +fi; + +echo "$as_me:$LINENO: checking for temporary directory" >&5 +echo $ECHO_N "checking for temporary directory... $ECHO_C" >&6 +if test -z "$with_tmpdir" || test yes = "$with_tmpdir"; then + for with_tmpdir in /tmp /var/tmp no; do + if test -d "$with_tmpdir" && test -x "$with_tmpdir" \ + && test -w "$with_tmpdir" && test -r "$with_tmpdir"; then + break + fi + done + if test no = "$with_tmpdir"; then + { echo "$as_me:$LINENO: WARNING: Failed to find usable temporary directory. Using '/tmp'." >&5 +echo "$as_me: WARNING: Failed to find usable temporary directory. Using '/tmp'." >&2;} + with_tmpdir=/tmp + fi + echo "$as_me:$LINENO: result: $with_tmpdir" >&5 +echo "${ECHO_T}$with_tmpdir" >&6 +elif ! echo "$with_tmpdir" |grep '^[\\/]'; then + echo "$as_me:$LINENO: result: $with_tmpdir" >&5 +echo "${ECHO_T}$with_tmpdir" >&6 + { { echo "$as_me:$LINENO: error: --with-tmpdir requires an absolute path." >&5 +echo "$as_me: error: --with-tmpdir requires an absolute path." >&2;} + { (exit 1); exit 1; }; } +elif ! test -d "$with_tmpdir" || ! test -x "$with_tmpdir" \ + || ! test -w "$with_tmpdir" || ! test -r "$with_tmpdir"; then + echo "$as_me:$LINENO: result: $with_tmpdir" >&5 +echo "${ECHO_T}$with_tmpdir" >&6 + { echo "$as_me:$LINENO: WARNING: User supplied temporary directory ('$with_tmpdir') does not + exist or lacks sufficient permissions for read/write." >&5 +echo "$as_me: WARNING: User supplied temporary directory ('$with_tmpdir') does not + exist or lacks sufficient permissions for read/write." >&2;} +fi + + +cat >>confdefs.h <<_ACEOF +#define TMPDIR_DFLT "$with_tmpdir" +_ACEOF + + + + + + +# Check whether --with-umask or --without-umask was given. +if test "${with_umask+set}" = set; then + withval="$with_umask" + +fi; + +if test -z "$with_umask" || test yes = "$with_umask"; then + with_umask=002 +elif test no = "$with_umask"; then + with_umask=000 +fi + + +cat >>confdefs.h <<_ACEOF +#define UMASK_DFLT $with_umask +_ACEOF + + + + +# Check whether --with-cvs-admin-group or --without-cvs-admin-group was given. +if test "${with_cvs_admin_group+set}" = set; then + withval="$with_cvs_admin_group" + +else + with_cvs_admin_group=cvsadmin +fi; + +if test yes = "$with_cvs_admin_group"; then + with_cvs_admin_group=cvsadmin +fi +if test no != "$with_cvs_admin_group"; then + +cat >>confdefs.h <<_ACEOF +#define CVS_ADMIN_GROUP "$with_cvs_admin_group" +_ACEOF + +fi + + +# Check whether --enable-cvs-ndbm or --disable-cvs-ndbm was given. +if test "${enable_cvs_ndbm+set}" = set; then + enableval="$enable_cvs_ndbm" + +else + enable_cvs_ndbm=yes +fi; +if test no != "$enable_cvs_ndbm"; then + +cat >>confdefs.h <<\_ACEOF +#define MY_NDBM 1 +_ACEOF + +fi + + + + + + + + + +# 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" + +else + if test "$ac_cv_search_connect" != no; then + enable_client=yes + fi +fi; +if test no != "$enable_client"; then + +cat >>confdefs.h <<\_ACEOF +#define CLIENT_SUPPORT 1 +_ACEOF + +fi + + + +# Check whether --enable-password-authenticated-client or --disable-password-authenticated-client was given. +if test "${enable_password_authenticated_client+set}" = set; then + enableval="$enable_password_authenticated_client" + +fi; + +if test no != "$enable_password_authenticated_client"; then + if test no != "$enable_client"; then + +cat >>confdefs.h <<\_ACEOF +#define AUTH_CLIENT_SUPPORT 1 +_ACEOF + + else + { echo "$as_me:$LINENO: WARNING: --enable-password-authenticated-client is meaningless with + the CVS client disabled (--disable-client)" >&5 +echo "$as_me: WARNING: --enable-password-authenticated-client is meaningless with + the CVS client disabled (--disable-client)" >&2;} + fi +fi + + + + +# Check whether --enable-server or --disable-server was given. +if test "${enable_server+set}" = set; then + enableval="$enable_server" + +else + if test "$ac_cv_search_connect" != no; then + enable_server=yes + fi +fi; + +if test no != "$enable_server"; then + +cat >>confdefs.h <<\_ACEOF +#define SERVER_SUPPORT 1 +_ACEOF + + + echo "$as_me:$LINENO: checking for library containing crypt" >&5 +echo $ECHO_N "checking for library containing crypt... $ECHO_C" >&6 +if test "${ac_cv_search_crypt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_crypt=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +crypt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_crypt="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_crypt" = no; then + for ac_lib in crypt; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* 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 () +{ +crypt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_crypt="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_crypt" >&5 +echo "${ECHO_T}$ac_cv_search_crypt" >&6 +if test "$ac_cv_search_crypt" != no; then + test "$ac_cv_search_crypt" = "none required" || LIBS="$ac_cv_search_crypt $LIBS" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CRYPT 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define AUTH_SERVER_SUPPORT 1 +_ACEOF + +fi + + # Check whether --enable-server-flow-control or --disable-server-flow-control was given. +if test "${enable_server_flow_control+set}" = set; then + enableval="$enable_server_flow_control" + if test yes = $enable_server_flow_control; then + enable_server_flow_control=1M,2M + fi +else + enable_server_flow_control=1M,2M +fi; + if test no != $enable_server_flow_control; then + ccvs_lwm=`expr "$enable_server_flow_control" : '\(.*\),'` + ccvs_hwm=`expr "$enable_server_flow_control" : '.*,\(.*\)'` + ccvs_lwm_E=`expr "$ccvs_lwm" : '[0-9][0-9]*\(.*\)'` + ccvs_lwm=`expr "$ccvs_lwm" : '\([0-9][0-9]*\)'` + test "" != "$ccvs_lwm" || ccvs_lwm_E="?" + case $ccvs_lwm_E in + G) ccvs_lwm="$ccvs_lwm * 1024 * 1024 * 1024";; + M) ccvs_lwm="$ccvs_lwm * 1024 * 1024";; + k) ccvs_lwm="$ccvs_lwm * 1024";; + b | '') ;; + *) { { echo "$as_me:$LINENO: error: Can't parse argument to --enable-server-flow-control + ('$enable_server_flow_control') as ," >&5 +echo "$as_me: error: Can't parse argument to --enable-server-flow-control + ('$enable_server_flow_control') as ," >&2;} + { (exit 1); exit 1; }; } + esac + ccvs_hwm_E=`expr "$ccvs_hwm" : '[0-9][0-9]*\(.*\)'` + ccvs_hwm=`expr "$ccvs_hwm" : '\([0-9][0-9]*\).*'` + test "" != "$ccvs_hwm" || ccvs_hwm_E="?" + case $ccvs_hwm_E in + G) ccvs_hwm="$ccvs_hwm * 1024 * 1024 * 1024";; + M) ccvs_hwm="$ccvs_hwm * 1024 * 1024";; + k) ccvs_hwm="$ccvs_hwm * 1024";; + b | '') ccvs_hwm="$ccvs_hwm";; + *) { { echo "$as_me:$LINENO: error: Can't parse argument to --enable-server-flow-control + ('$enable_server_flow_control') as ," >&5 +echo "$as_me: error: Can't parse argument to --enable-server-flow-control + ('$enable_server_flow_control') as ," >&2;} + { (exit 1); exit 1; }; } + esac + + +cat >>confdefs.h <<\_ACEOF +#define SERVER_FLOWCONTROL 1 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SERVER_LO_WATER ($ccvs_lwm) +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SERVER_HI_WATER ($ccvs_hwm) +_ACEOF + + fi # enable_server_flow_control +fi # enable_server + + + + +# Check whether --enable-pam or --disable-pam was given. +if test "${enable_pam+set}" = set; then + enableval="$enable_pam" + +else + enable_pam=no + +fi; + +if test yes = $enable_pam; then + if test "${ac_cv_header_security_pam_appl_h+set}" = set; then + echo "$as_me:$LINENO: checking for security/pam_appl.h" >&5 +echo $ECHO_N "checking for security/pam_appl.h... $ECHO_C" >&6 +if test "${ac_cv_header_security_pam_appl_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_security_pam_appl_h" >&5 +echo "${ECHO_T}$ac_cv_header_security_pam_appl_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking security/pam_appl.h usability" >&5 +echo $ECHO_N "checking security/pam_appl.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking security/pam_appl.h presence" >&5 +echo $ECHO_N "checking security/pam_appl.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: security/pam_appl.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: security/pam_appl.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: security/pam_appl.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: security/pam_appl.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: security/pam_appl.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: security/pam_appl.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: security/pam_appl.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: security/pam_appl.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: security/pam_appl.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: security/pam_appl.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: security/pam_appl.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: security/pam_appl.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: security/pam_appl.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: security/pam_appl.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: security/pam_appl.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: security/pam_appl.h: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to bug-cvs@gnu.org ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for security/pam_appl.h" >&5 +echo $ECHO_N "checking for security/pam_appl.h... $ECHO_C" >&6 +if test "${ac_cv_header_security_pam_appl_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_security_pam_appl_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_security_pam_appl_h" >&5 +echo "${ECHO_T}$ac_cv_header_security_pam_appl_h" >&6 + +fi +if test $ac_cv_header_security_pam_appl_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PAM 1 +_ACEOF + + echo "$as_me:$LINENO: checking for pam_start in -lpam" >&5 +echo $ECHO_N "checking for pam_start in -lpam... $ECHO_C" >&6 +if test "${ac_cv_lib_pam_pam_start+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpam $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pam_start (); +int +main () +{ +pam_start (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pam_pam_start=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pam_pam_start=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pam_pam_start" >&5 +echo "${ECHO_T}$ac_cv_lib_pam_pam_start" >&6 +if test $ac_cv_lib_pam_pam_start = yes; then + LIBS="${LIBS} -lpam" +else + { { echo "$as_me:$LINENO: error: Could not find PAM libraries but the headers exist. + Give the --disable-pam option to compile without PAM support (or fix + your broken configuration)" >&5 +echo "$as_me: error: Could not find PAM libraries but the headers exist. + Give the --disable-pam option to compile without PAM support (or fix + your broken configuration)" >&2;} + { (exit 1); exit 1; }; } + +fi + +else + { { echo "$as_me:$LINENO: error: Could not find PAM headers" >&5 +echo "$as_me: error: Could not find PAM headers" >&2;} + { (exit 1); exit 1; }; } + +fi + + +fi + + + + +# Check whether --enable-case-sensitivity or --disable-case-sensitivity was given. +if test "${enable_case_sensitivity+set}" = set; then + enableval="$enable_case_sensitivity" + case "$enable_case_sensitivity" in + yes | no | auto) ;; + *) + { { echo "$as_me:$LINENO: error: Unrecognized argument to --enable-case-sensitivity: \`$enable_case_sensitivity'. Acceptable values are \`yes', \`no', and \`auto'." >&5 +echo "$as_me: error: Unrecognized argument to --enable-case-sensitivity: \`$enable_case_sensitivity'. Acceptable values are \`yes', \`no', and \`auto'." >&2;} + { (exit 1); exit 1; }; } + ;; + esac +else + enable_case_sensitivity=auto +fi; + +acx_forced=' (forced)' +echo "$as_me:$LINENO: checking for a case sensitive file system" >&5 +echo $ECHO_N "checking for a case sensitive file system... $ECHO_C" >&6 +if test $enable_case_sensitivity = auto; then + if test "${acx_cv_case_sensitive+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f ac_TEST_filenames_CASE_sensitive + echo foo >ac_test_filenames_case_sensitive + if test -f ac_TEST_filenames_CASE_sensitive; then + acx_cv_case_sensitive=no + else + acx_cv_case_sensitive=yes + fi + rm ac_test_filenames_case_sensitive + +fi + + enable_case_sensitivity=$acx_cv_case_sensitive + acx_forced= +fi +echo "$as_me:$LINENO: result: $enable_case_sensitivity$acx_forced" >&5 +echo "${ECHO_T}$enable_case_sensitivity$acx_forced" >&6 +if test $enable_case_sensitivity = no; then + +cat >>confdefs.h <<\_ACEOF +#define FILENAMES_CASE_INSENSITIVE 1 +_ACEOF + + case $LIBOBJS in + "fncase.$ac_objext" | \ + *" fncase.$ac_objext" | \ + "fncase.$ac_objext "* | \ + *" fncase.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS fncase.$ac_objext" ;; +esac + +fi + + + + +# Check whether --enable-encryption or --disable-encryption was given. +if test "${enable_encryption+set}" = set; then + enableval="$enable_encryption" + +else + enable_encryption=no +fi; +if test "$enable_encryption" = yes; then + if test no != "$with_client" || test no != "$with_server"; then + +cat >>confdefs.h <<\_ACEOF +#define ENCRYPTION 1 +_ACEOF + + else + { echo "$as_me:$LINENO: WARNING: --enable-encryption is meaningless when neither the CVS client + nor the CVS server is enabled (--disable-client and --disable-server)." >&5 +echo "$as_me: WARNING: --enable-encryption is meaningless when neither the CVS client + nor the CVS server is enabled (--disable-client and --disable-server)." >&2;} + fi +fi + + + + +# Check whether --enable-force-editor or --disable-force-editor was given. +if test "${enable_force_editor+set}" = set; then + enableval="$enable_force_editor" + +else + enable_force_editor=no +fi; + +if test yes = "$enable_force_editor"; then + +cat >>confdefs.h <<\_ACEOF +#define FORCE_USE_EDITOR 1 +_ACEOF + +fi + + + + +# 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-lock-compatibility or --disable-lock-compatibility was given. +if test "${enable_lock_compatibility+set}" = set; then + enableval="$enable_lock_compatibility" + +else + enable_lock_compatibility=yes +fi; + +if test x$enable_lock_compatibility = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define LOCK_COMPATIBILITY 1 +_ACEOF + +fi + + + + +# Check whether --enable-rootcommit or --disable-rootcommit was given. +if test "${enable_rootcommit+set}" = set; then + enableval="$enable_rootcommit" + +else + enable_rootcommit=no +fi; +if test "$enable_rootcommit" = no; then + +cat >>confdefs.h <<\_ACEOF +#define CVS_BADROOT 1 +_ACEOF + +fi + + +# Check whether --enable-old-info-support or --disable-old-info-support was given. +if test "${enable_old_info_support+set}" = set; then + enableval="$enable_old_info_support" + +else + enable_old_info_format_support=yes +fi; +if test "$enable_old_info_format_support" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define SUPPORT_OLD_INFO_FMT_STRINGS 1 +_ACEOF + +fi + + + + + + + + +echo "$as_me:$LINENO: checking for cygwin32" >&5 +echo $ECHO_N "checking for cygwin32... $ECHO_C" >&6 +if test "${ccvs_cv_sys_cygwin32+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +return __CYGWIN32__; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ccvs_cv_sys_cygwin32=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ccvs_cv_sys_cygwin32=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ccvs_cv_sys_cygwin32" >&5 +echo "${ECHO_T}$ccvs_cv_sys_cygwin32" >&6 +if test $ccvs_cv_sys_cygwin32 = yes; then + LIBS="$LIBS -ladvapi32" + + +cat >>confdefs.h <<\_ACEOF +#define UTIME_EXPECTS_WRITABLE 1 +_ACEOF + + + +cat >>confdefs.h <<\_ACEOF +#define USE_SETMODE_STDOUT 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SETMODE 1 +_ACEOF + +fi + + ac_config_files="$ac_config_files contrib/validate_repo" + + ac_config_files="$ac_config_files contrib/clmerge" + + ac_config_files="$ac_config_files contrib/cln_hist" + + ac_config_files="$ac_config_files contrib/commit_prep" + + ac_config_files="$ac_config_files contrib/cvs_acls" + + ac_config_files="$ac_config_files contrib/log" + + ac_config_files="$ac_config_files contrib/log_accum" + + ac_config_files="$ac_config_files contrib/mfpipe" + + ac_config_files="$ac_config_files contrib/pvcs2rcs" + + ac_config_files="$ac_config_files contrib/rcs2log:contrib/rcs2log.sh" + + ac_config_files="$ac_config_files contrib/rcslock" + + ac_config_files="$ac_config_files contrib/sccs2rcs" + + ac_config_files="$ac_config_files doc/mkman" + + ac_config_files="$ac_config_files src/cvsbug" + + ac_config_files="$ac_config_files windows-NT/fix-msvc-mak:windows-NT/plhead.pl:windows-NT/fix-msvc-mak.pl" + + ac_config_files="$ac_config_files windows-NT/mkconfig:windows-NT/plhead.pl:windows-NT/mkconfig.pl" + + + ac_config_files="$ac_config_files Makefile contrib/Makefile cvs.spec diff/Makefile doc/Makefile emx/Makefile lib/Makefile m4/Makefile man/Makefile os2/Makefile src/Makefile src/sanity.config.sh tools/Makefile vms/Makefile windows-NT/Makefile windows-NT/SCC/Makefile zlib/Makefile" + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${MAKE_TARGETS_IN_VPATH_TRUE}" && test -z "${MAKE_TARGETS_IN_VPATH_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"MAKE_TARGETS_IN_VPATH\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"MAKE_TARGETS_IN_VPATH\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by Concurrent Versions System (CVS) $as_me 1.12.9, which was +generated by GNU Autoconf 2.58. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +Concurrent Versions System (CVS) config.status 1.12.9 +configured by $0, generated by GNU Autoconf 2.58, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS section. +# + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" +# Capture the value of obsolete ALL_LINGUAS because we need it to compute + # POFILES, GMOFILES, UPDATEPOFILES, DUMMYPOFILES, CATALOGS. But hide it + # from automake. + eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"' + # Capture the value of LINGUAS because we need it to compute CATALOGS. + LINGUAS="${LINGUAS-%UNSET%}" + + +_ACEOF + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "contrib/validate_repo" ) CONFIG_FILES="$CONFIG_FILES contrib/validate_repo" ;; + "contrib/clmerge" ) CONFIG_FILES="$CONFIG_FILES contrib/clmerge" ;; + "contrib/cln_hist" ) CONFIG_FILES="$CONFIG_FILES contrib/cln_hist" ;; + "contrib/commit_prep" ) CONFIG_FILES="$CONFIG_FILES contrib/commit_prep" ;; + "contrib/cvs_acls" ) CONFIG_FILES="$CONFIG_FILES contrib/cvs_acls" ;; + "contrib/log" ) CONFIG_FILES="$CONFIG_FILES contrib/log" ;; + "contrib/log_accum" ) CONFIG_FILES="$CONFIG_FILES contrib/log_accum" ;; + "contrib/mfpipe" ) CONFIG_FILES="$CONFIG_FILES contrib/mfpipe" ;; + "contrib/pvcs2rcs" ) CONFIG_FILES="$CONFIG_FILES contrib/pvcs2rcs" ;; + "contrib/rcs2log" ) CONFIG_FILES="$CONFIG_FILES contrib/rcs2log:contrib/rcs2log.sh" ;; + "contrib/rcslock" ) CONFIG_FILES="$CONFIG_FILES contrib/rcslock" ;; + "contrib/sccs2rcs" ) CONFIG_FILES="$CONFIG_FILES contrib/sccs2rcs" ;; + "doc/mkman" ) CONFIG_FILES="$CONFIG_FILES doc/mkman" ;; + "src/cvsbug" ) CONFIG_FILES="$CONFIG_FILES src/cvsbug" ;; + "windows-NT/fix-msvc-mak" ) CONFIG_FILES="$CONFIG_FILES windows-NT/fix-msvc-mak:windows-NT/plhead.pl:windows-NT/fix-msvc-mak.pl" ;; + "windows-NT/mkconfig" ) CONFIG_FILES="$CONFIG_FILES windows-NT/mkconfig:windows-NT/plhead.pl:windows-NT/mkconfig.pl" ;; + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "contrib/Makefile" ) CONFIG_FILES="$CONFIG_FILES contrib/Makefile" ;; + "cvs.spec" ) CONFIG_FILES="$CONFIG_FILES cvs.spec" ;; + "diff/Makefile" ) CONFIG_FILES="$CONFIG_FILES diff/Makefile" ;; + "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "emx/Makefile" ) CONFIG_FILES="$CONFIG_FILES emx/Makefile" ;; + "lib/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; + "m4/Makefile" ) CONFIG_FILES="$CONFIG_FILES m4/Makefile" ;; + "man/Makefile" ) CONFIG_FILES="$CONFIG_FILES man/Makefile" ;; + "os2/Makefile" ) CONFIG_FILES="$CONFIG_FILES os2/Makefile" ;; + "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/sanity.config.sh" ) CONFIG_FILES="$CONFIG_FILES src/sanity.config.sh" ;; + "tools/Makefile" ) CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; + "vms/Makefile" ) CONFIG_FILES="$CONFIG_FILES vms/Makefile" ;; + "windows-NT/Makefile" ) CONFIG_FILES="$CONFIG_FILES windows-NT/Makefile" ;; + "windows-NT/SCC/Makefile" ) CONFIG_FILES="$CONFIG_FILES windows-NT/SCC/Makefile" ;; + "zlib/Makefile" ) CONFIG_FILES="$CONFIG_FILES zlib/Makefile" ;; + "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "default-1" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default-1" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CYGPATH_W@,$CYGPATH_W,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@AMTAR@,$AMTAR,;t t +s,@install_sh@,$install_sh,;t t +s,@STRIP@,$STRIP,;t t +s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@am__leading_dot@,$am__leading_dot,;t t +s,@ac_prefix_program@,$ac_prefix_program,;t t +s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t +s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t +s,@MAINT@,$MAINT,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@CCDEPMODE@,$CCDEPMODE,;t t +s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t +s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@LN_S@,$LN_S,;t t +s,@PERL@,$PERL,;t t +s,@CSH@,$CSH,;t t +s,@MKTEMP@,$MKTEMP,;t t +s,@SENDMAIL@,$SENDMAIL,;t t +s,@PR@,$PR,;t t +s,@ROFF@,$ROFF,;t t +s,@PS2PDF@,$PS2PDF,;t t +s,@TEXI2DVI@,$TEXI2DVI,;t t +s,@MAKE_TARGETS_IN_VPATH_TRUE@,$MAKE_TARGETS_IN_VPATH_TRUE,;t t +s,@MAKE_TARGETS_IN_VPATH_FALSE@,$MAKE_TARGETS_IN_VPATH_FALSE,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@MKINSTALLDIRS@,$MKINSTALLDIRS,;t t +s,@USE_NLS@,$USE_NLS,;t t +s,@MSGFMT@,$MSGFMT,;t t +s,@GMSGFMT@,$GMSGFMT,;t t +s,@XGETTEXT@,$XGETTEXT,;t t +s,@MSGMERGE@,$MSGMERGE,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@LIBICONV@,$LIBICONV,;t t +s,@LTLIBICONV@,$LTLIBICONV,;t t +s,@INTLLIBS@,$INTLLIBS,;t t +s,@LIBINTL@,$LIBINTL,;t t +s,@LTLIBINTL@,$LTLIBINTL,;t t +s,@POSUB@,$POSUB,;t t +s,@STDBOOL_H@,$STDBOOL_H,;t t +s,@HAVE__BOOL@,$HAVE__BOOL,;t t +s,@ALLOCA@,$ALLOCA,;t t +s,@ALLOCA_H@,$ALLOCA_H,;t t +s,@FNMATCH_H@,$FNMATCH_H,;t t +s,@LIB_NANOSLEEP@,$LIB_NANOSLEEP,;t t +s,@YACC@,$YACC,;t t +s,@YFLAGS@,$YFLAGS,;t t +s,@LIB_CLOCK_GETTIME@,$LIB_CLOCK_GETTIME,;t t +s,@HAVE_PUTENV@,$HAVE_PUTENV,;t t +s,@cvs_client_objects@,$cvs_client_objects,;t t +s,@KRB4@,$KRB4,;t t +s,@ZLIB_SUBDIRS@,$ZLIB_SUBDIRS,;t t +s,@ZLIB_CPPFLAGS@,$ZLIB_CPPFLAGS,;t t +s,@ZLIB_LIBS@,$ZLIB_LIBS,;t t +s,@with_default_rsh@,$with_default_rsh,;t t +s,@RSH_DFLT@,$RSH_DFLT,;t t +s,@EDITOR@,$EDITOR,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +/@MKTEMP_SH_FUNCTION@/r $MKTEMP_SH_FUNCTION +s,@MKTEMP_SH_FUNCTION@,,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +case "$ac_dir" in +.) ac_abs_builddir=$ac_builddir;; +*) + case $ac_builddir in + .) ac_abs_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_builddir=$ac_builddir;; + *) ac_abs_builddir="$ac_dir"/$ac_builddir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir="$ac_dir"/${ac_top_builddir}.;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir="$ac_dir"/$ac_srcdir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir="$ac_dir"/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + + # Run the commands associated with the file. + case $ac_file in + contrib/validate_repo ) chmod +x contrib/validate_repo ;; + contrib/clmerge ) chmod +x contrib/clmerge ;; + contrib/cln_hist ) chmod +x contrib/cln_hist ;; + contrib/commit_prep ) chmod +x contrib/commit_prep ;; + contrib/cvs_acls ) chmod +x contrib/cvs_acls ;; + contrib/log ) chmod +x contrib/log ;; + contrib/log_accum ) chmod +x contrib/log_accum ;; + contrib/mfpipe ) chmod +x contrib/mfpipe ;; + contrib/pvcs2rcs ) chmod +x contrib/pvcs2rcs ;; + contrib/rcs2log ) chmod +x contrib/rcs2log ;; + contrib/rcslock ) chmod +x contrib/rcslock ;; + contrib/sccs2rcs ) chmod +x contrib/sccs2rcs ;; + doc/mkman ) chmod +x doc/mkman ;; + src/cvsbug ) chmod +x src/cvsbug ;; + windows-NT/fix-msvc-mak ) chmod +x windows-NT/fix-msvc-mak ;; + windows-NT/mkconfig ) chmod +x windows-NT/mkconfig ;; + esac +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +# Compute $ac_file's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $ac_file | $ac_file:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null || +$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X$ac_file : 'X\(//\)[^/]' \| \ + X$ac_file : 'X\(//\)$' \| \ + X$ac_file : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X$ac_file | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/stamp-h$_am_stamp_count +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_dir=`(dirname "$ac_dest") 2>/dev/null || +$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_dest" : 'X\(//\)[^/]' \| \ + X"$ac_dest" : 'X\(//\)$' \| \ + X"$ac_dest" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_dest" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +case "$ac_dir" in +.) ac_abs_builddir=$ac_builddir;; +*) + case $ac_builddir in + .) ac_abs_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_builddir=$ac_builddir;; + *) ac_abs_builddir="$ac_dir"/$ac_builddir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir="$ac_dir"/${ac_top_builddir}.;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir="$ac_dir"/$ac_srcdir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir="$ac_dir"/$ac_top_srcdir;; + esac;; +esac + + + { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 +echo "$as_me: executing $ac_dest commands" >&6;} + case $ac_dest in + depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`(dirname "$mf") 2>/dev/null || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + else + continue + fi + grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`(dirname "$file") 2>/dev/null || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p $dirpart/$fdir + else + as_dir=$dirpart/$fdir + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5 +echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;} + { (exit 1); exit 1; }; }; } + + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + default-1 ) + for ac_file in $CONFIG_FILES; do + # Support "outfile[:infile[:infile...]]" + case "$ac_file" in + *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + esac + # PO directories have a Makefile.in generated from Makefile.in.in. + case "$ac_file" in */Makefile.in) + # Adjust a relative srcdir. + ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` + ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`" + ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` + # In autoconf-2.13 it is called $ac_given_srcdir. + # In autoconf-2.50 it is called $srcdir. + test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" + case "$ac_given_srcdir" in + .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; + /*) top_srcdir="$ac_given_srcdir" ;; + *) top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then + rm -f "$ac_dir/POTFILES" + test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES" + cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES" + POMAKEFILEDEPS="POTFILES.in" + # ALL_LINGUAS, POFILES, GMOFILES, UPDATEPOFILES, DUMMYPOFILES depend + # on $ac_dir but don't depend on user-specified configuration + # parameters. + if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then + # The LINGUAS file contains the set of available languages. + if test -n "$OBSOLETE_ALL_LINGUAS"; then + test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete" + fi + ALL_LINGUAS_=`sed -e "/^#/d" "$ac_given_srcdir/$ac_dir/LINGUAS"` + # Hide the ALL_LINGUAS assigment from automake. + eval 'ALL_LINGUAS''=$ALL_LINGUAS_' + POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS" + else + # The set of available languages was given in configure.in. + eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS' + fi + case "$ac_given_srcdir" in + .) srcdirpre= ;; + *) srcdirpre='$(srcdir)/' ;; + esac + POFILES= + GMOFILES= + UPDATEPOFILES= + DUMMYPOFILES= + for lang in $ALL_LINGUAS; do + POFILES="$POFILES $srcdirpre$lang.po" + GMOFILES="$GMOFILES $srcdirpre$lang.gmo" + UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" + DUMMYPOFILES="$DUMMYPOFILES $lang.nop" + done + # CATALOGS depends on both $ac_dir and the user's LINGUAS + # environment variable. + INST_LINGUAS= + if test -n "$ALL_LINGUAS"; then + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "$LINGUAS"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + INST_LINGUAS="$INST_LINGUAS $presentlang" + fi + done + fi + CATALOGS= + if test -n "$INST_LINGUAS"; then + for lang in $INST_LINGUAS; do + CATALOGS="$CATALOGS $lang.gmo" + done + fi + test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile" + sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile" + for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do + if test -f "$f"; then + case "$f" in + *.orig | *.bak | *~) ;; + *) cat "$f" >> "$ac_dir/Makefile" ;; + esac + fi + done + fi + ;; + esac + done ;; + esac +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + + + + +# Report the state of this version of CVS if this is from dev. + diff --git a/contrib/cvs-1.12.9/contrib/README b/contrib/cvs-1.12.9/contrib/README new file mode 100644 index 0000000000..b6e33f7ec1 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/README @@ -0,0 +1,133 @@ +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. + +USE AT YOUR OWN RISK -- and all that stuff. + +"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. + +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. + + check_cvs A perl script to check an entire repository for + corruption. + Contributed by Donald Sharp . + + clmerge A perl script to handle merge conflicts in GNU + style ChangeLog files . + Contributed by Tom Tromey . + + cln_hist A perl script to compress your + $CVSROOT/CVSROOT/history file, as it can grow quite + large after extended use. + Contributed by David G. Grubbs + + 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 + single log message, and mail the result somewhere. + Can also do other checks for $Id and that you are + committing the correct revision of the file. + Read the comments carefully. + Contributed by David Hampton . + + 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 . + + cvs_acls A perl script that implements Access Control Lists + by using the "commitinfo" hook provided with the + "cvs commit" command. + Contributed by David G. Grubbs . + + cvscheck Identifies files added, changed, or removed in a + cvscheck.man checked out CVS tree; also notices unknown files. + Contributed by Lowell Skoog + + cvshelp.man An introductory manual page written by Lowell Skoog + . It is most likely + out-of-date relative to CVS 1.3, but still may be + useful. + + debug_check_log A shell script to help analyze sanity check failures. + Contributed by Derek R. Price + + + descend A shell script that can be used to recursively + descend.man descend through a directory. In CVS 1.2, this was + very useful, since many of the commands were not + recursive. In CVS 1.3 (and later), however, most of + the commands are recursive. However, this may still + come in handy. + Contributed by Lowell Skoog + + dirfns A shar file which contains some code that might + help your system support opendir/readdir/closedir, + if it does not already. + Copied from the C-News distribution. + + intro.doc A user's view of what you need to know to get + started with CVS. + Contributed by . + + log A perl script suitable for including in your + $CVSROOT/CVSROOT/loginfo file for logging commit + changes. Includes the RCS revision of the change + as part of the log. + Contributed by Kevin Samborn . + + log_accum See commit_prep. + + mfpipe Another perl script for logging. Allows you to + pipe the log message to a file and/or send mail + to some alias. + Contributed by John Clyne . + + pvcs2rcs A perl script to convert a PVCS tree to an RCS tree. + + rcs-to-cvs Script to import sources that may have been under + RCS control already. + Contributed by Per Cederqvist . + + rcs2log A shell script to create a ChangeLog-format file + given only a set of RCS files. + Contributed by Paul Eggert . + + rcs2sccs A shell script to convert simple RCS files into + SCCS files, originally gleaned off the network + somewhere (originally by "kenc") and modified by + Jerry Jelinek and + Brian Berliner to increase + robustness and add support for one-level of branches. + + rcslock A perl script that can be added to your commitinfo + file that tries to determine if your RCS file is + currently locked by someone else, as might be the + case for a binary file. + Contributed by John Rouillard . + + sccs2rcs A C-shell script that can convert (some) SCCS files + into RCS files, retaining the info contained in the + SCCS file (like dates, author, and log message). + Contributed by Ken Cox . diff --git a/contrib/cvs-1.12.9/contrib/clmerge.in b/contrib/cvs-1.12.9/contrib/clmerge.in new file mode 100644 index 0000000000..5e5251ac1b --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/clmerge.in @@ -0,0 +1,152 @@ +#! @PERL@ + +# Merge conflicted ChangeLogs +# tromey Mon Aug 15 1994 + +# Usage is: +# +# cl-merge [-i] file ... +# +# With -i, it works in place (backups put in a ~ file). Otherwise the +# merged ChangeLog is printed to stdout. + +# Please report any bugs to me. I wrote this yesterday, so there are no +# guarantees about its performance. I recommend checking its output +# carefully. If you do send a bug report, please include the failing +# ChangeLog, so I can include it in my test suite. +# +# Tom +# --- +# tromey@busco.lanl.gov Member, League for Programming Freedom +# Sadism and farce are always inexplicably linked. +# -- Alexander Theroux + + +# Month->number mapping. Used for sorting. +%months = ('Jan', 0, + 'Feb', 1, + 'Mar', 2, + 'Apr', 3, + 'May', 4, + 'Jun', 5, + 'Jul', 6, + 'Aug', 7, + 'Sep', 8, + 'Oct', 9, + 'Nov', 10, + 'Dec', 11); + +# If '-i' is given, do it in-place. +if ($ARGV[0] eq '-i') { + shift (@ARGV); + $^I = '~'; +} + +$lastkey = ''; +$lastval = ''; +$conf = 0; +%conflist = (); + +$tjd = 0; + +# Simple state machine. The states: +# +# 0 Not in conflict. Just copy input to output. +# 1 Beginning an entry. Next non-blank line is key. +# 2 In entry. Entry beginner transitions to state 1. +while (<>) { + if (/^<<<>>>/) { + # End of conflict. Output. + + # Copy last key into array. + if ($lastkey ne '') { + $conflist{$lastkey} = $lastval; + + $lastkey = ''; + $lastval = ''; + } + + foreach (reverse sort clcmp keys %conflist) { + print STDERR "doing $_" if $tjd; + print $_; + print $conflist{$_}; + } + + $lastkey = ''; + $lastval = ''; + $conf = 0; + %conflist = (); + } elsif ($conf == 1) { + # Beginning an entry. Skip empty lines. Error if not a real + # beginner. + if (/^$/) { + # Empty line; just skip at this point. + } elsif (/^[MTWFS]/) { + # Looks like the name of a day; assume opener and move to + # "in entry" state. + $lastkey = $_; + $conf = 2; + print STDERR "found $_" if $tjd; + } else { + die ("conflict crosses entry boundaries: $_"); + } + } elsif ($conf == 2) { + # In entry. Copy into variable until we see beginner line. + if (/^[MTWFS]/) { + # Entry beginner line. + + # Copy last key into array. + if ($lastkey ne '') { + $conflist{$lastkey} = $lastval; + + $lastkey = ''; + $lastval = ''; + } + + $lastkey = $_; + print STDERR "found $_" if $tjd; + $lastval = ''; + } else { + $lastval .= $_; + } + } else { + # Just copy. + print; + } +} + +# Compare ChangeLog time strings like <=>. +# +# 0 1 2 3 +# Thu Aug 11 13:22:42 1994 Tom Tromey (tromey@creche.colorado.edu) +# 0123456789012345678901234567890 +# +sub clcmp { + # First check year. + $r = substr ($a, 20, 4) <=> substr ($b, 20, 4); + + # Now check month. + $r = $months{substr ($a, 4, 3)} <=> $months{substr ($b, 4, 3)} if !$r; + + # Now check day. + $r = substr ($a, 8, 2) <=> substr ($b, 8, 2) if !$r; + + # Now check time (3 parts). + $r = substr ($a, 11, 2) <=> substr ($b, 11, 2) if !$r; + $r = substr ($a, 14, 2) <=> substr ($b, 14, 2) if !$r; + $r = substr ($a, 17, 2) <=> substr ($b, 17, 2) if !$r; + + $r; +} diff --git a/contrib/cvs-1.12.9/contrib/cln_hist.in b/contrib/cvs-1.12.9/contrib/cln_hist.in new file mode 100644 index 0000000000..d9818471cf --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/cln_hist.in @@ -0,0 +1,91 @@ +#! @PERL@ +# -*-Perl-*- +# +# Contributed by David G. Grubbs +# +# Clean up the history file. 10 Record types: MAR OFT WUCG +# +# WUCG records are thrown out. +# MAR records are retained. +# T records: retain only last tag with same combined tag/module. +# +# Two passes: Walk through the first time and remember the +# 1. Last Tag record with same "tag" and "module" names. +# 2. Last O record with unique user/module/directory, unless followed +# by a matching F record. +# + +$r = $ENV{"CVSROOT"}; +$c = "$r/CVSROOT"; +$h = "$c/history"; + +eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';" + while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV)); +exit 255 if $die; # process any variable=value switches + +%tags = (); +%outs = (); + +# +# Move history file to safe place and re-initialize a new one. +# +rename($h, "$h.bak"); +open(XX, ">$h"); +close(XX); + +# +# Pass1 -- remember last tag and checkout. +# +open(HIST, "$h.bak"); +while () { + next if /^[MARWUCG]/; + + # Save whole line keyed by tag|module + if (/^T/) { + @tmp = split(/\|/, $_); + $tags{$tmp[4] . '|' . $tmp[5]} = $_; + } + # Save whole line + if (/^[OF]/) { + @tmp = split(/\|/, $_); + $outs{$tmp[1] . '|' . $tmp[2] . '|' . $tmp[5]} = $_; + } +} + +# +# Pass2 -- print out what we want to save. +# +open(SAVE, ">$h.work"); +open(HIST, "$h.bak"); +while () { + next if /^[FWUCG]/; + + # If whole line matches saved (i.e. "last") one, print it. + if (/^T/) { + @tmp = split(/\|/, $_); + next if $tags{$tmp[4] . '|' . $tmp[5]} ne $_; + } + # Save whole line + if (/^O/) { + @tmp = split(/\|/, $_); + next if $outs{$tmp[1] . '|' . $tmp[2] . '|' . $tmp[5]} ne $_; + } + + print SAVE $_; +} + +# +# Put back the saved stuff +# +system "cat $h >> $h.work"; + +if (-s $h) { + rename ($h, "$h.interim"); + print "history.interim has non-zero size.\n"; +} else { + unlink($h); +} + +rename ("$h.work", $h); + +exit(0); diff --git a/contrib/cvs-1.12.9/contrib/commit_prep.in b/contrib/cvs-1.12.9/contrib/commit_prep.in new file mode 100644 index 0000000000..b5f73ac87e --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/commit_prep.in @@ -0,0 +1,56 @@ +#! @PERL@ +# -*-Perl-*- +# +# Perl filter to handle pre-commit checking of files. This program +# records the last directory where commits will be taking place for +# use by the log_accum.pl script. +# +# IMPORTANT: this script interacts with log_accum, they have to agree +# on the tmpfile name to use. See $LAST_FILE below. +# +# Contributed by David Hampton +# Stripped to minimum by Roy Fielding +# +############################################################ +$TMPDIR = $ENV{'TMPDIR'} || '/tmp'; +$FILE_PREFIX = '#cvs.'; + +# If see a "-u $USER" argument, then destructively remove it from the +# argument list, so $ARGV[0] will be the repository dir again, as it +# used to be before we added the -u flag. +if ($ARGV[0] eq '-u') { + shift @ARGV; + $CVS_USERNAME = shift (@ARGV); +} + +# This needs to match the corresponding var in log_accum.pl, including +# the appending of the pgrp and username suffixes (see uses of this +# var farther down). +$LAST_FILE = "$TMPDIR/${FILE_PREFIX}lastdir"; + +sub write_line { + my ($filename, $line) = @_; + +# A check of some kind is needed here, but the rules aren't apparent +# at the moment: + +# foreach($filename, $line){ +# $_ =~ m#^([-\@\w.\#]+)$#; +# $_ = $1; +# } + + open(FILE, ">$filename") || die("Cannot open $filename: $!\n"); + print(FILE $line, "\n"); + close(FILE); +} + +# +# Record this directory as the last one checked. This will be used +# by the log_accumulate script to determine when it is processing +# the final directory of a multi-directory commit. +# +$id = getpgrp(); + +&write_line("$LAST_FILE.$id.$CVS_USERNAME", $ARGV[0]); + +exit(0); diff --git a/contrib/cvs-1.12.9/contrib/cvs2vendor.sh b/contrib/cvs-1.12.9/contrib/cvs2vendor.sh new file mode 100644 index 0000000000..d58ab31af6 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/cvs2vendor.sh @@ -0,0 +1,147 @@ +#! /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 , 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 + +if sort -k 1,1 /dev/null 2>/dev/null +then sort_each_field='-k 1 -k 2 -k 3 -k 4 -k 5 -k 6 -k 7 -k 8 -k 9' +else sort_each_field='+0 +1 +2 +3 +4 +5 +6 +7 +8' +fi + +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 $sort_each_field | 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-1.12.9/contrib/cvs_acls.in b/contrib/cvs-1.12.9/contrib/cvs_acls.in new file mode 100644 index 0000000000..990f2076c7 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/cvs_acls.in @@ -0,0 +1,193 @@ +#! @PERL@ +# -*-Perl-*- +# +# Access control lists for CVS. dgg@ksr.com (David G. Grubbs) +# Branch specific controls added by voisine@bytemobile.com (Aaron Voisine) +# +# CVS "commitinfo" for matching repository names, running the program it finds +# on the same line. More information is available in the CVS man pages. +# +# ==== INSTALLATION: +# +# To use this program as I intended, do the following four things: +# +# 0. Install PERL. :-) +# +# 1. Put one line, as the *only* non-comment line, in your commitinfo file: +# +# DEFAULT /usr/local/bin/cvs_acls +# +# 2. Install this file as /usr/local/bin/cvs_acls and make it executable. +# +# 3. Create a file named CVSROOT/avail and optionally add it to +# CVSROOT/checkoutlist and check it in. See the CVS manual's +# administrative files section about checkoutlist. Typically: +# +# $ cvs checkout CVSROOT +# $ cd CVSROOT +# [ create the avail file ] +# [ add avail to checkoutlist ] +# $ cvs add avail +# $ cvs commit -m 'Added avail for use with cvs_acls.' avail checkoutlist +# +# ==== FORMAT OF THE avail FILE: +# +# The avail file determines whether you may commit files. It contains lines +# read from top to bottom, keeping track of a single "bit". The "bit" +# defaults to "on". It can be turned "off" by "unavail" lines and "on" by +# "avail" lines. ==> Last one counts. +# +# Any line not beginning with "avail" or "unavail" is ignored. +# +# Lines beginning with "avail" or "unavail" are assumed to be '|'-separated +# triples: (All spaces and tabs are ignored in a line.) +# +# {avail.*,unavail.*} [|user,user,... [|repos,repos,... [|branch,branch,...]]] +# +# 1. String starting with "avail" or "unavail". +# 2. Optional, comma-separated list of usernames. +# 3. Optional, comma-separated list of repository pathnames. +# These are pathnames relative to $CVSROOT. They can be directories or +# filenames. A directory name allows access to all files and +# directories below it. +# 4. Optional, comma-separated list of branch tags. +# If not specified, all branches are assumed. Use HEAD to reference the +# main branch. +# +# Example: (Text from the ';;' rightward may not appear in the file.) +# +# unavail ;; Make whole repository unavailable. +# avail|dgg ;; Except for user "dgg". +# avail|fred, john|bin/ls ;; Except when "fred" or "john" commit to +# ;; the module whose repository is "bin/ls" +# avail|ed|/bin/ls|stable ;; Except when "ed" commits to the "stable" +# ;; branch of the "bin/ls" repository +# +# PROGRAM LOGIC: +# +# CVS passes to @ARGV an absolute directory pathname (the repository +# appended to your $CVSROOT variable), followed by a list of filenames +# within that directory. +# +# We walk through the avail file looking for a line that matches the +# username, repository and branch. +# +# A username match is simply the user's name appearing in the second +# column of the avail line in a space-or-comma separate list. +# +# A repository match is either: +# - One element of the third column matches $ARGV[0], or some +# parent directory of $ARGV[0]. +# - Otherwise *all* file arguments ($ARGV[1..$#ARGV]) must be +# in the file list in one avail line. +# - In other words, using directory names in the third column of +# the avail file allows committing of any file (or group of +# files in a single commit) in the tree below that directory. +# - If individual file names are used in the third column of +# the avail file, then files must be committed individually or +# all files specified in a single commit must all appear in +# third column of a single avail line. +# +# A branch match is either: +# - When no branches are listed in the fourth column. +# - One element from the fourth column matches each of the tag +# names for $ARGV[1..$#ARGV] found in the CVS/Entries file. +# - HEAD specified in the fourth column will match if there +# is no tag listed in the CVS/Entries file. +# + +$debug = 0; +$cvsroot = $ENV{'CVSROOT'}; +$availfile = $cvsroot . "/CVSROOT/avail"; +$entries = "CVS/Entries"; +$myname = $ENV{"USER"} if !($myname = $ENV{"LOGNAME"}); + +eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';" + while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV)); +exit 255 if $die; # process any variable=value switches + +die "Must set CVSROOT\n" if !$cvsroot; +($repos = shift) =~ s:^$cvsroot/::; +grep($_ = $repos . '/' . $_, @ARGV); + +print "$$ Repos: $repos\n","$$ ==== ",join("\n$$ ==== ",@ARGV),"\n" if $debug; + +$exit_val = 0; # Good Exit value + +$universal_off = 0; + +my %branch; +my $f; + +open(ENTRIES, $entries) || die("Cannot open $entries.\n"); +while() { + chop; + next if /^\s*$/; + if(m|^[^/]*/([^/]*)/(?:[^/]*/)*[^/]?([^/]*)$|) { + $branch{$repos . '/' . $1} = ($2) ? $2 : "HEAD"; + print "$$ $1/$2\n" if $debug; + } +} +close(ENTRIES); + +open (AVAIL, $availfile) || exit(0); # It is ok for avail file not to exist +while () { + chop; + next if /^\s*\#/; + next if /^\s*$/; + ($flagstr, $u, $m, $b) = split(/[\s,]*\|[\s,]*/, $_); + + # Skip anything not starting with "avail" or "unavail" and complain. + (print "Bad avail line: $_\n"), next + if ($flagstr !~ /^avail/ && $flagstr !~ /^unavail/); + + # Set which bit we are playing with. ('0' is OK == Available). + $flag = (($& eq "avail") ? 0 : 1); + + # If we find a "universal off" flag (i.e. a simple "unavail") remember it + $universal_off = 1 if ($flag && !$u && !$m && !$b); + + # $myname considered "in user list" if actually in list or is NULL + $in_user = (!$u || grep ($_ eq $myname, split(/[\s,]+/,$u))); + print "$$ \$myname($myname) in user list: $_\n" if $debug && $in_user; + + # Module matches if it is a NULL module list in the avail line. If module + # list is not null, we check every argument combination. + if (!($in_repo = !$m)) { + my @tmp = split(/[\s,]+/,$m); + for $j (@tmp) { + # If the repos from avail is a parent(or equal) dir of $repos, OK + $in_repo = 1, last if ($repos eq $j || $repos =~ /^$j\//); + } + if (!$in_repo) { + $in_repo = 1; + for $j (@ARGV) { + last if !($in_repo = grep ($_ eq $j, @tmp)); + } + } + } + print "$$ \$repos($repos) in repository list: $_\n" if $debug && $in_repo; + + # Branch matches if it is in the branch list in the avail line, the branch + # list is NULL, or there is no branch and HEAD is in the branch list. + if(!($in_branch = !$b)) { + @bls = split (/[\s,]+/,$b); + + for $j (@ARGV) { + $f = $j; + last if !($in_branch = grep($_ eq $branch{$j}, @bls)); + } + } + print "$$ \$branch($branch{$f}) in branch list: $_\n" + if $debug && $in_branch; + + $exit_val = $flag if ($in_user && $in_repo && $in_branch); + print "$$ ==== \$exit_val = $exit_val\n$$ ==== \$flag = $flag\n" if $debug; +} +close(AVAIL); +print "$$ ==== \$exit_val = $exit_val\n" if $debug; +print "**** Access denied: Insufficient Karma ($myname|$repos|$branch{$f})\n" + if $exit_val; +print "**** Access allowed: Personal Karma exceeds Environmental Karma.\n" + if $universal_off && !$exit_val; +exit($exit_val); diff --git a/contrib/cvs-1.12.9/contrib/cvshelp.man b/contrib/cvs-1.12.9/contrib/cvshelp.man new file mode 100644 index 0000000000..b166af6952 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/cvshelp.man @@ -0,0 +1,561 @@ +.\" Contributed by Lowell Skoog +.\" Full space in nroff; half space in troff +.de SP +.if n .sp +.if t .sp .5 +.. +.\" Start a command example +.de XS +.SP +.in +.5i +.ft B +.nf +.. +.\" End a command example +.de XE +.fi +.ft P +.in -.5i +.SP +.. +.TH CVSHELP LOCAL "17 March 1991" FLUKE +.SH NAME +cvshelp \- advice on using the Concurrent Versions System +.SH DESCRIPTION +This man page is based on experience using CVS. +It is bound to change as we gain more experience. +If you come up with better advice than is found here, +contact the Software Technology +Group and we will add it to this page. +.SS "Getting Started" +Use the following steps to prepare to use CVS: +.TP +\(bu +Take a look at the CVS manual page to see what it can do for you, and +if it fits your environment (or can possibly be made to fit your +environment). +.XS +man cvs +.XE +If things look good, continue on... +.TP +\(bu +Setup the master source repository. Choose a directory with +ample disk space available for source files. This is where the RCS +`,v' files will be stored. Say you choose +.B /src/master +as the root +of your source repository. Make the +.SB CVSROOT.adm +directory in the root of the source repository: +.XS +mkdir /src/master/CVSROOT.adm +.XE +.TP +\(bu +Populate this directory with the +.I loginfo +and +.I modules +files from the +.B "/usr/doc/local/cvs" +directory. Edit these files to reflect your local source repository +environment \- they may be quite small initially, but will grow as +sources are added to your source repository. Turn these files into +RCS controlled files: +.XS +cd /src/master/CVSROOT.adm +ci \-m'Initial loginfo file' loginfo +ci \-m'Initial modules file' modules +.XE +.TP +\(bu +Run the command: +.XS +mkmodules /src/master/CVSROOT.adm +.XE +This will build the +.BR ndbm (3) +file for the modules database. +.TP +\(bu +Remember to edit the +.I modules +file manually when sources are checked +in with +.B checkin +or CVS +.BR add . +A copy of the +.I modules +file for editing can be retrieved with the command: +.XS +cvs checkout CVSROOT.adm +.XE +.TP +\(bu +Have all users of the CVS system set the +.SM CVSROOT +environment variable appropriately to reflect the placement of your +source repository. If the above example is used, the following +commands can be placed in a +.I .login +or +.I .profile +file: +.XS +setenv CVSROOT /src/master +.XE +for csh users, and +.XS +CVSROOT=/src/master; export CVSROOT +.XE +for sh users. +.SS "Placing Locally Written Sources Under CVS Control" +Say you want to place the `whizbang' sources under +CVS control. Say further that the sources have never +been under revision control before. +.TP +\(bu +Move the source hierarchy (lock, stock, and barrel) +into the master source repository: +.XS +mv ~/whizbang $CVSROOT +.XE +.TP +\(bu +Clean out unwanted object files: +.XS +cd $CVSROOT/whizbang +make clean +.XE +.TP +\(bu +Turn every file in the hierarchy into an RCS controlled file: +.XS +descend \-f 'ci \-t/dev/null \-m"Placed under CVS control" \-nV\fR\fIx\fR\fB_\fR\fIy\fR\fB *' +.XE +In this example, the initial release tag is \fBV\fIx\fB_\fIy\fR, +representing version \fIx\fR.\fIy\fR. +.LP +You can use CVS on sources that are already under RCS control. +The following example shows how. +In this example, the source package is called `skunkworks'. +.TP +\(bu +Move the source hierarchy into the master source +repository: +.XS +mv ~/skunkworks $CVSROOT +.XE +.TP +\(bu +Clean out unwanted object files: +.XS +cd $CVSROOT/skunkworks +make clean +.XE +.TP +\(bu +Clean out unwanted working files, leaving only the RCS `,v' files: +.XS +descend \-r rcsclean +.XE +Note: If any working files have been checked out and changed, +.B rcsclean +will fail. Check in the modified working files +and run the command again. +.TP +\(bu +Get rid of +.SB RCS +subdirectories. CVS does not use them. +.XS +descend \-r \-f 'mv RCS/*,v .' +descend \-r \-f 'rmdir RCS' +.XE +.TP +\(bu +Delete any unwanted files that remain in the source hierarchy. Then +make sure all files are under RCS control: +.XS +descend \-f 'ci \-t/dev/null \-m"Placed under CVS control" \-n\fR\fItag\fR\fB *' +.XE +.I tag +is the latest symbolic revision tag that you applied to your package +(if any). Note: This command will probably generate lots of error +messages (for directories and existing RCS files) that you can +ignore. +.SS "Placing a Third-Party Source Distribution Under CVS Control" +The +.B checkin +command checks third-party sources into CVS. The +difference between third-party sources and locally +written sources is that third-party sources must be checked into a +separate branch (called the +.IR "vendor branch" ) +of the RCS tree. This makes it possible to merge local changes to +the sources with later releases from the vendor. +.TP +\(bu +Save the original distribution kit somewhere. For example, if the +master source repository is +.B /src/master +the distribution kit could be saved in +.BR /src/dist . +Organize the distribution directory so that each release +is clearly identifiable. +.TP +\(bu +Unpack the package in a scratch directory, for example +.BR ~/scratch . +.TP +\(bu +Create a repository for the package. +In this example, the package is called `Bugs-R-Us 4.3'. +.XS +mkdir $CVSROOT/bugs +.XE +.TP +\(bu +Check in the unpacked files: +.XS +cd ~/scratch +checkin \-m 'Bugs-R-Us 4.3 distribution' bugs VENDOR V4_3 +.XE +There is nothing magic about the tag `VENDOR', which is applied to +the vendor branch. You can use whatever tag you want. `VENDOR' is a +useful convention. +.TP +\(bu +Never modify vendor files before checking them in. +Check in the files +.I exactly +as you unpacked them. +If you check in locally modified files, future vendor releases may +wipe out your local changes. +.SS "Working With CVS-Controlled Sources" +To use or edit the sources, you must check out a private copy. +For the following examples, the master files are assumed to reside in +.BR "$CVSROOT/behemoth" . +The working directory is +.BR "~/work" . +See +.BR cvs (local) +for more details on the commands mentioned below. +.TP +.I "To Check Out Working Files +Use CVS +.BR checkout : +.XS +cd ~/work +cvs checkout behemoth +.XE +There is nothing magic about the working directory. CVS will check +out sources anywhere you like. Once you have a working copy of the +sources, you can compile or edit them as desired. +.TP +.I "To Display Changes You Have Made" +Use CVS +.BR diff +to display detailed changes, equivalent to +.BR rcsdiff (local). +You can also use +.BR cvscheck (local) +to list files added, changed, and removed in +the directory, but not yet +.BR commit ted. +You must be in a directory containing working files. +.TP +.I "To Display Revision Information" +Use CVS +.BR log , +which is equivalent to +.BR rlog (local). +You must be in a directory containing working files. +.TP +.I "To Update Working Files" +Use CVS +.BR update +in a directory containing working files. +This command brings your working files up +to date with changes checked into the +master repository since you last checked out or updated +your files. +.TP +.I "To Check In Your Changes" +Use CVS +.BR commit +in a directory containing working files. +This command checks your changes into the master repository. +You can specify files by name or use +.XS +cvs commit \-a +.XE +to +.B commit +all the files you have changed. +.TP +.I "To Add a File" +Add the file to the working directory. +Use CVS +.B add +to mark the file as added. +Use CVS +.B commit +to add the file to the master repository. +.TP +.I "To Remove a File" +Remove the file from the working directory. +Use CVS +.B remove +to mark the file as removed. +Use CVS +.B commit +to move the file from its current location in the master repository +to the CVS +.IR Attic +directory. +.TP +.I "To Add a Directory" +Add the directory to the working directory. +Use CVS +.B add +to add the directory to the master repository. +.TP +.I "To Remove a Directory" +.br +You shouldn't remove directories under CVS. You should instead remove +their contents and then prune them (using the +.B \-f +and +.B \-p +options) when you +.B checkout +or +.B update +your working files. +.TP +.I "To Tag a Release" +Use CVS +.B tag +to apply a symbolic tag to the latest revision of each file in the +master repository. For example: +.XS +cvs tag V2_1 behemoth +.XE +.TP +.I "To Retrieve an Exact Copy of a Previous Release" +During a CVS +.B checkout +or +.BR update , +use the +.B \-r +option to retrieve revisions associated with a symbolic tag. +Use the +.B \-f +option to ignore all RCS files that do not contain the +tag. +Use the +.B \-p +option to prune directories that wind up empty because none +of their files matched the tag. Example: +.XS +cd ~/work +cvs checkout \-r V2_1 \-f \-p behemoth +.XE +.SS "Logging Changes" +It is a good idea to keep a change log together with the +sources. As a minimum, the change log should name and describe each +tagged release. The change log should also be under CVS control and +should be tagged along with the sources. +.LP +.BR cvslog (local) +can help. This command logs +changes reported during CVS +.B commit +operations. It automatically +updates a change log file in your working directory. When you are +finished making changes, you (optionally) edit the change log file and +then commit it to the master repository. +.LP +Note: You must edit the change log to describe a new release +and +.B commit +it to the master repository +.I before +.BR tag ging +the release using CVS. Otherwise, the release description will not be +included in the tagged package. +.LP +See +.BR cvslog (local) +for more information. +.SS "Merging a Subsequent Third-Party Distribution" +The initial steps in this process are identical to placing a +third-party distribution under CVS for the first time: save the +distribution kit and unpack the package in a scratch directory. From +that point the steps diverge. +The following example considers release 5.0 of the +Bugs-R-Us package. +.TP +\(bu +Check in the sources after unpacking them: +.XS +cd ~/scratch +checkin \-m 'Bugs-R-Us 5.0 distribution' bugs VENDOR V5_0 \\ + | tee ~/WARNINGS +.XE +It is important to save the output of +.B checkin +in a file +because it lists the sources that have been locally modified. +It is best to save the file in a different directory (for example, +your home directory). Otherwise, +.B checkin +will try to check it into the master repository. +.TP +\(bu +In your usual working directory, check out a fresh copy of the +distribution that you just checked in. +.XS +cd ~/work +cvs checkout \-r VENDOR bugs +.XE +The +.B checkout +command shown above retrieves the latest revision on the vendor branch. +.TP +\(bu +See the `WARNINGS' file for a list of all locally modified +sources. +For each locally modified source, +look at the differences between +the new distribution and the latest local revision: +.XS +cvs diff \-r \fR\fILocalRev file\fR\fB +.XE +In this command, +.I LocalRev +is the latest +numeric or symbolic revision +on the RCS trunk of +.IR file . +You can use CVS +.B log +to get the revision history. +.TP +\(bu +If your local modifications to a file have been incorporated into +the vendor's distribution, then you should reset the default RCS +branch for that file to the vendor branch. CVS doesn't provide a +mechanism to do this. You have to do it by hand in the master +repository: +.XS +rcs \-bVENDOR \fR\fIfile\fR\fB,v +.XE +.TP +\(bu +If your local modifications need to be merged with the +new distribution, use CVS +.B join +to do it: +.XS +cvs join \-r VENDOR \fR\fIfile\fR\fB +.XE +The resulting file will be placed in your working directory. +Edit it to resolve any overlaps. +.TP +\(bu +Test the merged package. +.TP +\(bu +Commit all modified files to the repository: +.XS +cvs commit \-a +.XE +.TP +\(bu +Tag the repository with a new local tag. +.SS "Applying Patches to Third-Party Sources" +Patches are handled in a manner very similar to complete +third-party distributions. This example considers patches applied to +Bugs-R-Us release 5.0. +.TP +\(bu +Save the patch files together with the distribution kit +to which they apply. +The patch file names should clearly indicate the patch +level. +.TP +\(bu +In a scratch directory, check out the last `clean' vendor copy \- the +highest revision on the vendor branch with +.IR "no local changes" : +.XS +cd ~/scratch +cvs checkout \-r VENDOR bugs +.XE +.TP +\(bu +Use +.BR patch (local) +to apply the patches. You should now have an image of the +vendor's software just as though you had received a complete, +new release. +.TP +\(bu +Proceed with the steps described for merging a subsequent third-party +distribution. +.TP +\(bu +Note: When you get to the step that requires you +to check out the new distribution after you have +checked it into the vendor branch, you should move to a different +directory. Do not attempt to +.B checkout +files in the directory in +which you applied the patches. If you do, CVS will try to merge the +changes that you made during patching with the version being checked +out and things will get very confusing. Instead, +go to a different directory (like your working directory) and +check out the files there. +.SS "Advice to Third-Party Source Hackers" +As you can see from the preceding sections, merging local changes +into third-party distributions remains difficult, and probably +always will. This fact suggests some guidelines: +.TP +\(bu +Minimize local changes. +.I Never +make stylistic changes. +Change makefiles only as much as needed for installation. Avoid +overhauling anything. Pray that the vendor does the same. +.TP +\(bu +Avoid renaming files or moving them around. +.TP +\(bu +Put independent, locally written files like help documents, local +tools, or man pages in a sub-directory called `local-additions'. +Locally written files that are linked into an existing executable +should be added right in with the vendor's sources (not in a +`local-additions' directory). +If, in the future, +the vendor distributes something +equivalent to your locally written files +you can CVS +.B remove +the files from the `local-additions' directory at that time. +.SH SEE ALSO +.BR cvs (local), +.BR checkin (local), +.BR cvslog (local), +.BR cvscheck (local) +.SH AUTHOR +Lowell Skoog +.br +Software Technology Group +.br +Technical Computing diff --git a/contrib/cvs-1.12.9/contrib/descend.man b/contrib/cvs-1.12.9/contrib/descend.man new file mode 100644 index 0000000000..0434ca8b43 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/descend.man @@ -0,0 +1,114 @@ +.TH DESCEND 1 "31 March 1992" +.SH NAME +descend \- walk directory tree and execute a command at each node +.SH SYNOPSIS +.B descend +[ +.B \-afqrv +] +.I command +[ +.I directory +\&.\|.\|. +] +.SH DESCRIPTION +.B descend +walks down a directory tree and executes a command at each node. It +is not as versatile as +.BR find (1), +but it has a simpler syntax. If no +.I directory +is specified, +.B descend +starts at the current one. +.LP +Unlike +.BR find , +.B descend +can be told to skip the special directories associated with RCS, +CVS, and SCCS. This makes +.B descend +especially handy for use with these packages. It can be used with +other commands too, of course. +.LP +.B descend +is a poor man's way to make any command recursive. Note: +.B descend +does not follow symbolic links to directories unless they are +specified on the command line. +.SH OPTIONS +.TP 15 +.B \-a +.I All. +Descend into directories that begin with '.'. +.TP +.B \-f +.I Force. +Ignore errors during descent. Normally, +.B descend +quits when an error occurs. +.TP +.B \-q +.I Quiet. +Suppress the message `In directory +.IR directory ' +that is normally printed during the descent. +.TP +.B \-r +.I Restricted. +Don't descend into the special directories +.SB RCS, +.SB CVS, +.SB CVS.adm, +and +.SB SCCS. +.TP +.B \-v +.I Verbose. +Print +.I command +before executing it. +.SH EXAMPLES +.TP 15 +.B "descend ls" +Cheap substitute for `ls -R'. +.TP 15 +.B "descend -f 'rm *' tree" +Strip `tree' of its leaves. This command descends the `tree' +directory, removing all regular files. Since +.BR rm (1) +does not remove directories, this command leaves the directory +structure of `tree' intact, but denuded. The +.B \-f +option is required to keep +.B descend +from quitting. You could use `rm \-f' instead. +.TP +.B "descend -r 'co RCS/*'" /project/src/ +Check out every RCS file under the directory +.BR "/project/src" . +.TP +.B "descend -r 'cvs diff'" +Perform CVS `diff' operation on every directory below (and including) +the current one. +.SH DIAGNOSTICS +Returns 1 if errors occur (and the +.B \-f +option is not used). Otherwise returns 0. +.SH SEE ALSO +.BR find (1), +.BR rcsintro (1), +.BR cvs (1), +.BR sccs (1) +.SH AUTHOR +Lowell Skoog +.br +Software Technology Group +.br +John Fluke Mfg. Co., Inc. +.SH BUGS +Shell metacharacters in +.I command +may have bizarre effects. In particular, compound commands +(containing ';', '[', and ']' characters) will not work. It is best +to enclose complicated commands in single quotes \(aa\ \(aa. diff --git a/contrib/cvs-1.12.9/contrib/intro.doc b/contrib/cvs-1.12.9/contrib/intro.doc new file mode 100644 index 0000000000..a6d4ec1233 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/intro.doc @@ -0,0 +1,112 @@ +Date: Tue, 16 Jun 1992 17:05:23 +0200 +From: Steven.Pemberton@cwi.nl +Message-Id: <9206161505.AA06927.steven@sijs.cwi.nl> +To: berliner@Sun.COM +Subject: cvs + +INTRODUCTION TO USING CVS + + CVS is a system that lets groups of people work simultaneously on + groups of files (for instance program sources). + + It works by holding a central 'repository' of the most recent version + of the files. You may at any time create a personal copy of these + files; if at a later date newer versions of the files are put in the + repository, you can 'update' your copy. + + You may edit your copy of the files freely. If new versions of the + files have been put in the repository in the meantime, doing an update + merges the changes in the central copy into your copy. + (It can be that when you do an update, the changes in the + central copy clash with changes you have made in your own + copy. In this case cvs warns you, and you have to resolve the + clash in your copy.) + + When you are satisfied with the changes you have made in your copy of + the files, you can 'commit' them into the central repository. + (When you do a commit, if you haven't updated to the most + recent version of the files, cvs tells you this; then you have + to first update, resolve any possible clashes, and then redo + the commit.) + +USING CVS + + Suppose that a number of repositories have been stored in + /usr/src/cvs. Whenever you use cvs, the environment variable + CVSROOT must be set to this (for some reason): + + CVSROOT=/usr/src/cvs + export CVSROOT + +TO CREATE A PERSONAL COPY OF A REPOSITORY + + Suppose you want a copy of the files in repository 'views' to be + created in your directory src. Go to the place where you want your + copy of the directory, and do a 'checkout' of the directory you + want: + + cd $HOME/src + cvs checkout views + + This creates a directory called (in this case) 'views' in the src + directory, containing a copy of the files, which you may now work + on to your heart's content. + +TO UPDATE YOUR COPY + + Use the command 'cvs update'. + + This will update your copy with any changes from the central + repository, telling you which files have been updated (their names + are displayed with a U before them), and which have been modified + by you and not yet committed (preceded by an M). You will be + warned of any files that contain clashes, the clashes will be + marked in the file surrounded by lines of the form <<<< and >>>>. + +TO COMMIT YOUR CHANGES + + Use the command 'cvs commit'. + + You will be put in an editor to make a message that describes the + changes that you have made (for future reference). Your changes + will then be added to the central copy. + +ADDING AND REMOVING FILES + + It can be that the changes you want to make involve a completely + new file, or removing an existing one. The commands to use here + are: + + cvs add + cvs remove + + You still have to do a commit after these commands. You may make + any number of new files in your copy of the repository, but they + will not be committed to the central copy unless you do a 'cvs add'. + +OTHER USEFUL COMMANDS AND HINTS + + To see the commit messages for files, and who made them, use: + + cvs log [filenames] + + To see the differences between your version and the central version: + + cvs diff [filenames] + + To give a file a new name, rename it and do an add and a remove. + + To lose your changes and go back to the version from the + repository, delete the file and do an update. + + After an update where there have been clashes, your original + version of the file is saved as .#file.version. + + All the cvs commands mentioned accept a flag '-n', that doesn't do + the action, but lets you see what would happen. For instance, you + can use 'cvs -n update' to see which files would be updated. + +MORE INFORMATION + + This is necessarily a very brief introduction. See the manual page + (man cvs) for full details. diff --git a/contrib/cvs-1.12.9/contrib/log.in b/contrib/cvs-1.12.9/contrib/log.in new file mode 100644 index 0000000000..87f40b55b5 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/log.in @@ -0,0 +1,208 @@ +#! @PERL@ +# -*-Perl-*- +# +# XXX: FIXME: handle multiple '-f logfile' arguments +# +# XXX -- I HATE Perl! This *will* be re-written in shell/awk/sed soon! +# + +# Usage: log.pl [-u user] [[-m mailto] ...] [-s] [-V] -f logfile 'dirname file ...' +# +# -u user - $USER passed from loginfo +# -m mailto - for each user to receive cvs log reports +# (multiple -m's permitted) +# -s - to prevent "cvs status -v" messages +# -V - without '-s', don't pass '-v' to cvs status +# -f logfile - for the logfile to append to (mandatory, +# but only one logfile can be specified). + +# here is what the output looks like: +# +# From: woods@kuma.domain.top +# Subject: CVS update: testmodule +# +# Date: Wednesday November 23, 1994 @ 14:15 +# Author: woods +# +# Update of /local/src-CVS/testmodule +# In directory kuma:/home/kuma/woods/work.d/testmodule +# +# Modified Files: +# test3 +# Added Files: +# test6 +# Removed Files: +# test4 +# Log Message: +# - wow, what a test +# +# (and for each file the "cvs status -v" output is appended unless -s is used) +# +# ================================================================== +# File: test3 Status: Up-to-date +# +# Working revision: 1.41 Wed Nov 23 14:15:59 1994 +# Repository revision: 1.41 /local/src-CVS/cvs/testmodule/test3,v +# Sticky Options: -ko +# +# Existing Tags: +# local-v2 (revision: 1.7) +# local-v1 (revision: 1.1.1.2) +# CVS-1_4A2 (revision: 1.1.1.2) +# local-v0 (revision: 1.2) +# CVS-1_4A1 (revision: 1.1.1.1) +# CVS (branch: 1.1.1) + +use strict; +use IO::File; + +my $cvsroot = $ENV{'CVSROOT'}; + +# turn off setgid +# +$) = $(; + +my $dostatus = 1; +my $verbosestatus = 1; +my $users; +my $login; +my $donefiles; +my $logfile; +my @files; + +# parse command line arguments +# +while (@ARGV) { + my $arg = shift @ARGV; + + if ($arg eq '-m') { + $users = "$users " . shift @ARGV; + } elsif ($arg eq '-u') { + $login = shift @ARGV; + } elsif ($arg eq '-f') { + ($logfile) && die "Too many '-f' args"; + $logfile = shift @ARGV; + } elsif ($arg eq '-s') { + $dostatus = 0; + } elsif ($arg eq '-V') { + $verbosestatus = 0; + } else { + ($donefiles) && die "Too many arguments!\n"; + $donefiles = 1; + @files = split(/ /, $arg); + } +} + +# the first argument is the module location relative to $CVSROOT +# +my $modulepath = shift @files; + +my $mailcmd = "| Mail -s 'CVS update: $modulepath'"; + +# Initialise some date and time arrays +# +my @mos = ('January','February','March','April','May','June','July', + 'August','September','October','November','December'); +my @days = ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'); + +my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime; +$year += 1900; + +# get a login name for the guy doing the commit.... +# +if ($login eq '') { + $login = getlogin || (getpwuid($<))[0] || "nobody"; +} + +# open log file for appending +# +my $logfh = new IO::File ">>" . $logfile + or die "Could not open(" . $logfile . "): $!\n"; + +# send mail, if there's anyone to send to! +# +my $mailfh; +if ($users) { + $mailcmd = "$mailcmd $users"; + $mailfh = new IO::File $mailcmd + or die "Could not Exec($mailcmd): $!\n"; +} + +# print out the log Header +# +$logfh->print ("\n"); +$logfh->print ("****************************************\n"); +$logfh->print ("Date:\t$days[$wday] $mos[$mon] $mday, $year @ $hour:" . sprintf("%02d", $min) . "\n"); +$logfh->print ("Author:\t$login\n\n"); + +if ($mailfh) { + $mailfh->print ("\n"); + $mailfh->print ("Date:\t$days[$wday] $mos[$mon] $mday, $year @ $hour:" . sprintf("%02d", $min) . "\n"); + $mailfh->print ("Author:\t$login\n\n"); +} + +# print the stuff from logmsg that comes in on stdin to the logfile +# +my $infh = new IO::File "< -"; +foreach ($infh->getlines) { + $logfh->print; + if ($mailfh) { + $mailfh->print ($_); + } +} +undef $infh; + +$logfh->print ("\n"); + +# after log information, do an 'cvs -Qq status -v' on each file in the arguments. +# +if ($dostatus != 0) { + while (@files) { + my $file = shift @files; + if ($file eq "-") { + $logfh->print ("[input file was '-']\n"); + if ($mailfh) { + $mailfh->print ("[input file was '-']\n"); + } + last; + } + my $rcsfh = new IO::File; + my $pid = $rcsfh->open ("-|"); + if ( !defined $pid ) + { + die "fork failed: $!"; + } + if ($pid == 0) + { + my @command = ('cvs', '-nQq', 'status'); + if ($verbosestatus) + { + push @command, '-v'; + } + push @command, $file; + exec @command; + die "cvs exec failed: $!"; + } + my $line; + while ($line = $rcsfh->getline) { + $logfh->print ($line); + if ($mailfh) { + $mailfh->print ($line); + } + } + undef $rcsfh; + } +} + +$logfh->close() + or die "Write to $logfile failed: $!"; + +if ($mailfh) +{ + $mailfh->close; + die "Pipe to $mailcmd failed" if $?; +} + +## must exit cleanly +## +exit 0; diff --git a/contrib/cvs-1.12.9/contrib/log_accum.in b/contrib/cvs-1.12.9/contrib/log_accum.in new file mode 100644 index 0000000000..f8d624c5d6 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/log_accum.in @@ -0,0 +1,720 @@ +#! @PERL@ +# -*-Perl-*- +# +# Perl filter to handle the log messages from the checkin of files in +# a directory. This script will group the lists of files by log +# message, and mail a single consolidated log message at the end of +# the commit. +# +# This file assumes a pre-commit checking program that leaves the +# names of the first and last commit directories in a temporary file. +# +# IMPORTANT: what the above means is, this script interacts with +# commit_prep, in that they have to agree on the tmpfile name to use. +# See $LAST_FILE below. +# +# How this works: CVS triggers this script once for each directory +# involved in the commit -- in other words, a single commit can invoke +# this script N times. It knows when it's on the last invocation by +# examining the contents of $LAST_FILE. Between invocations, it +# caches information for its future incarnations in various temporary +# files in /tmp, which are named according to the process group and +# the committer (by themselves, neither of these are unique, but +# together they almost always are, unless the same user is doing two +# commits simultaneously). The final invocation is the one that +# actually sends the mail -- it gathers up the cached information, +# combines that with what it found out on this pass, and sends a +# commit message to the appropriate mailing list. +# +# (Ask Karl Fogel if questions.) +# +# Contributed by David Hampton +# Roy Fielding removed useless code and added log/mail of new files +# Ken Coar added special processing (i.e., no diffs) for binary files +# + +############################################################ +# +# Configurable options +# +############################################################ +# +# Where do you want the RCS ID and delta info? +# 0 = none, +# 1 = in mail only, +# 2 = in both mail and logs. +# +$rcsidinfo = 2; + +#if you are using CVS web then set this to some value... if not set it to "" +# +# When set properly, this will cause links to aspects of the project to +# print in the commit emails. +#$CVSWEB_SCHEME = "http"; +#$CVSWEB_DOMAIN = "cvshome.org"; +#$CVSWEB_PORT = "80"; +#$CVSWEB_URI = "source/browse/"; +#$SEND_URL = "true"; +$SEND_DIFF = "true"; + + +# Set this to a domain to have CVS pretend that all users who make +# commits have mail accounts within that domain. +#$EMULATE_LOCAL_MAIL_USER="cvshome.org"; + +# Set this to '-c' for context diffs; defaults to '-u' for unidiff format. +$difftype = '-uN'; + +############################################################ +# +# Constants +# +############################################################ +$STATE_NONE = 0; +$STATE_CHANGED = 1; +$STATE_ADDED = 2; +$STATE_REMOVED = 3; +$STATE_LOG = 4; + +$TMPDIR = $ENV{'TMPDIR'} || '/tmp'; +$FILE_PREFIX = '#cvs.'; + +$LAST_FILE = "$TMPDIR/${FILE_PREFIX}lastdir"; # Created by commit_prep! +$ADDED_FILE = "$TMPDIR/${FILE_PREFIX}files.added"; +$REMOVED_FILE = "$TMPDIR/${FILE_PREFIX}files.removed"; +$LOG_FILE = "$TMPDIR/${FILE_PREFIX}files.log"; +$BRANCH_FILE = "$TMPDIR/${FILE_PREFIX}files.branch"; +$MLIST_FILE = "$TMPDIR/${FILE_PREFIX}files.mlist"; +$SUMMARY_FILE = "$TMPDIR/${FILE_PREFIX}files.summary"; + +$CVSROOT = $ENV{'CVSROOT'}; + +$MAIL_CMD = "| /usr/lib/sendmail -i -t"; +#$MAIL_CMD = "| /var/qmail/bin/qmail-inject"; +$MAIL_FROM = 'commitlogger'; #not needed if EMULATE_LOCAL_MAIL_USER +$SUBJECT_PRE = 'CVS update:'; + + +############################################################ +# +# Subroutines +# +############################################################ + +sub format_names { + local($dir, @files) = @_; + local(@lines); + + $lines[0] = sprintf(" %-08s", $dir); + foreach $file (@files) { + if (length($lines[$#lines]) + length($file) > 60) { + $lines[++$#lines] = sprintf(" %8s", " "); + } + $lines[$#lines] .= " ".$file; + } + @lines; +} + +sub cleanup_tmpfiles { + local(@files); + + opendir(DIR, $TMPDIR); + push(@files, grep(/^${FILE_PREFIX}.*\.${id}\.${cvs_user}$/, readdir(DIR))); + closedir(DIR); + foreach (@files) { + unlink "$TMPDIR/$_"; + } +} + +sub write_logfile { + local($filename, @lines) = @_; + + open(FILE, ">$filename") || die ("Cannot open log file $filename: $!\n"); + print(FILE join("\n", @lines), "\n"); + close(FILE); +} + +sub append_to_file { + local($filename, $dir, @files) = @_; + + if (@files) { + local(@lines) = &format_names($dir, @files); + open(FILE, ">>$filename") || die ("Cannot open file $filename: $!\n"); + print(FILE join("\n", @lines), "\n"); + close(FILE); + } +} + +sub write_line { + local($filename, $line) = @_; + + open(FILE, ">$filename") || die("Cannot open file $filename: $!\n"); + print(FILE $line, "\n"); + close(FILE); +} + +sub append_line { + local($filename, $line) = @_; + + open(FILE, ">>$filename") || die("Cannot open file $filename: $!\n"); + print(FILE $line, "\n"); + close(FILE); +} + +sub read_line { + local($filename) = @_; + local($line); + + open(FILE, "<$filename") || die("Cannot open file $filename: $!\n"); + $line = ; + close(FILE); + chomp($line); + $line; +} + +sub read_line_nodie { + local($filename) = @_; + local($line); + open(FILE, "<$filename") || return (""); + + $line = ; + close(FILE); + chomp($line); + $line; +} + +sub read_file_lines { + local($filename) = @_; + local(@text) = (); + + open(FILE, "<$filename") || return (); + while () { + chomp; + push(@text, $_); + } + close(FILE); + @text; +} + +sub read_file { + local($filename, $leader) = @_; + local(@text) = (); + + open(FILE, "<$filename") || return (); + while () { + chomp; + push(@text, sprintf(" %-10s %s", $leader, $_)); + $leader = ""; + } + close(FILE); + @text; +} + +sub read_logfile { + local($filename, $leader) = @_; + local(@text) = (); + + open(FILE, "<$filename") || die ("Cannot open log file $filename: $!\n"); + while () { + chomp; + push(@text, $leader.$_); + } + close(FILE); + @text; +} + +# +# do an 'cvs -Qn status' on each file in the arguments, and extract info. +# +sub change_summary { + local($out, @filenames) = @_; + local(@revline); + local($file, $rev, $rcsfile, $line, $vhost, $cvsweb_base); + + while (@filenames) { + $file = shift @filenames; + + if ("$file" eq "") { + next; + } + + open(RCS, "-|") || exec "$cvsbin/cvs", '-Qn', 'status', '--', $file; + + $rev = ""; + $delta = ""; + $rcsfile = ""; + + + while () { + if (/^[ \t]*Repository revision/) { + chomp; + @revline = split(' ', $_); + $rev = $revline[2]; + $rcsfile = $revline[3]; + $rcsfile =~ s,^$CVSROOT/,,; + $rcsfile =~ s/,v$//; + } + } + close(RCS); + + + if ($rev ne '' && $rcsfile ne '') { + open(RCS, "-|") || exec "$cvsbin/cvs", '-Qn', 'log', "-r$rev", + '--', $file; + while () { + if (/^date:/) { + chomp; + $delta = $_; + $delta =~ s/^.*;//; + $delta =~ s/^[\s]+lines://; + } + } + close(RCS); + } + + $diff = "\n\n"; + $vhost = @path[0]; + if ($CVSWEB_PORT eq "80") { + $cvsweb_base = "$CVSWEB_SCHEME://$vhost.$CVSWEB_DOMAIN/$CVSWEB_URI"; + } + else { + $cvsweb_base = "$CVSWEB_SCHEME://$vhost.$CVSWEB_DOMAIN:$CVSWEB_PORT/$CVSWEB_URI"; + } + if ($SEND_URL eq "true") { + $diff .= $cvsweb_base . join("/", @path) . "/$file"; + } + + # + # If this is a binary file, don't try to report a diff; not only is + # it meaningless, but it also screws up some mailers. We rely on + # Perl's 'is this binary' algorithm; it's pretty good. But not + # perfect. + # + if (($file =~ /\.(?:pdf|gif|jpg|mpg)$/i) || (-B $file)) { + if ($SEND_URL eq "true") { + $diff .= "?rev=$rev&content-type=text/x-cvsweb-markup\n\n"; + } + if ($SEND_DIFF eq "true") { + $diff .= "\t<>\n\n"; + } + } + else { + # + # Get the differences between this and the previous revision, + # being aware that new files always have revision '1.1' and + # new branches always end in '.n.1'. + # + if ($rev =~ /^(.*)\.([0-9]+)$/) { + $prev = $2 - 1; + $prev_rev = $1 . '.' . $prev; + + $prev_rev =~ s/\.[0-9]+\.0$//;# Truncate if first rev on branch + + if ($rev eq '1.1') { + if ($SEND_URL eq "true") { + $diff .= "?rev=$rev&content-type=text/x-cvsweb-markup\n\n"; + } + if ($SEND_DIFF eq "true") { + open(DIFF, "-|") + || exec "$cvsbin/cvs", '-Qn', 'update', '-p', '-r1.1', + '--', $file; + $diff .= "Index: $file\n==================================" + . "=================================\n"; + } + } + else { + if ($SEND_URL eq "true") { + $diff .= ".diff?r1=$prev_rev&r2=$rev\n\n"; + } + if ($SEND_DIFF eq "true") { + $diff .= "(In the diff below, changes in quantity " + . "of whitespace are not shown.)\n\n"; + open(DIFF, "-|") + || exec "$cvsbin/cvs", '-Qn', 'diff', "$difftype", + '-b', "-r$prev_rev", "-r$rev", '--', $file; + } + } + + if ($SEND_DIFF eq "true") { + while () { + $diff .= $_; + } + close(DIFF); + } + $diff .= "\n\n"; + } + } + + &append_line($out, sprintf("%-9s%-12s%s%s", $rev, $delta, + $rcsfile, $diff)); + } +} + + +sub build_header { + local($header); + delete $ENV{'TZ'}; + local($sec,$min,$hour,$mday,$mon,$year) = localtime(time); + + $header = sprintf(" User: %-8s\n Date: %02d/%02d/%02d %02d:%02d:%02d", + $cvs_user, $year%100, $mon+1, $mday, + $hour, $min, $sec); +# $header = sprintf("%-8s %02d/%02d/%02d %02d:%02d:%02d", +# $login, $year%100, $mon+1, $mday, +# $hour, $min, $sec); +} + +# !!! Destination Mailing-list and history file mappings here !!! + +#sub mlist_map +#{ +# local($path) = @_; +# my $domain = "cvshome.org"; +# +# if ($path =~ /^([^\/]+)/) { +# return "cvs\@$1.$domain"; +# } else { +# return "cvs\@$domain"; +# } +#} + +sub derive_subject_from_changes_file () +{ + my $subj = ""; + + for ($i = 0; ; $i++) + { + open (CH, "<$CHANGED_FILE.$i.$id.$cvs_user") or last; + + while (my $change = ) + { + # A changes file looks like this: + # + # src foo.c newfile.html + # www index.html project_nav.html + # + # Each line is " Dir File1 File2 ..." + # We only care about Dir, since the subject line should + # summarize. + + $change =~ s/^[ \t]*//; + $change =~ /^([^ \t]+)[ \t]*/; + my $dir = $1; + # Fold to rightmost directory component + $dir =~ /([^\/]+)$/; + $dir = $1; + if ($subj eq "") { + $subj = $dir; + } else { + $subj .= ", $dir"; + } + } + close (CH); + } + + if ($subj ne "") { + $subj = "MODIFIED: $subj ..."; + } + else { + # NPM: See if there's any file-addition notifications. + my $added = &read_line_nodie("$ADDED_FILE.$i.$id.$cvs_user"); + if ($added ne "") { + $subj .= "ADDED: $added "; + } + +# print "derive_subject_from_changes_file().. added== $added \n"; + + ## NPM: See if there's any file-removal notications. + my $removed = &read_line_nodie("$REMOVED_FILE.$i.$id.$cvs_user"); + if ($removed ne "") { + $subj .= "REMOVED: $removed "; + } + +# print "derive_subject_from_changes_file().. removed== $removed \n"; + + ## NPM: See if there's any branch notifications. + my $branched = &read_line_nodie("$BRANCH_FILE.$i.$id.$cvs_user"); + if ($branched ne "") { + $subj .= "BRANCHED: $branched"; + } + +# print "derive_subject_from_changes_file().. branched== $branched \n"; + + ## NPM: DEFAULT: DIRECTORY CREATION (c.f. "Check for a new directory first" in main mody) + if ($subj eq "") { + my $subject = join("/", @path); + $subj = "NEW: $subject"; + } + } + + return $subj; +} + +sub mail_notification +{ + local($addr_list, @text) = @_; + local($mail_to); + + my $subj = &derive_subject_from_changes_file (); + + if ($EMULATE_LOCAL_MAIL_USER NE "") { + $MAIL_FROM = "$cvs_user\@$EMULATE_LOCAL_MAIL_USER"; + } + + $mail_to = join(", ", @{$addr_list}); + + print "Mailing the commit message to $mail_to (from $MAIL_FROM)\n"; + + $ENV{'MAILUSER'} = $MAIL_FROM; + # Commented out on hocus, so comment it out here. -kff + # $ENV{'QMAILINJECT'} = 'f'; + + open(MAIL, "$MAIL_CMD -f$MAIL_FROM"); + print MAIL "From: $MAIL_FROM\n"; + print MAIL "To: $mail_to\n"; + print MAIL "Subject: $SUBJECT_PRE $subj\n\n"; + print(MAIL join("\n", @text)); + close(MAIL); +# print "Mailing the commit message to $MAIL_TO...\n"; +# +# #added by jrobbins@collab.net 1999/12/15 +# # attempt to get rid of anonymous +# $ENV{'MAILUSER'} = 'commitlogger'; +# $ENV{'QMAILINJECT'} = 'f'; +# +# open(MAIL, "| /var/qmail/bin/qmail-inject"); +# print(MAIL "To: $MAIL_TO\n"); +# print(MAIL "Subject: cvs commit: $ARGV[0]\n"); +# print(MAIL join("\n", @text)); +# close(MAIL); +} + +## process the command line arguments sent to this script +## it returns an array of files, %s, sent from the loginfo +## command +sub process_argv +{ + local(@argv) = @_; + local(@files); + local($arg); + print "Processing log script arguments...\n"; + + while (@argv) { + $arg = shift @argv; + + if ($arg eq '-u') { + $cvs_user = shift @argv; + } else { + ($donefiles) && die "Too many arguments!\n"; + $donefiles = 1; + $ARGV[0] = $arg; + @files = split(' ', $arg); + } + } + return @files; +} + + +############################################################# +# +# Main Body +# +############################################################ +# +# Setup environment +# +umask (002); + +# Connect to the database +$cvsbin = "/usr/bin"; + +# +# Initialize basic variables +# +$id = getpgrp(); +$state = $STATE_NONE; +$cvs_user = $ENV{'USER'} || getlogin || (getpwuid($<))[0] || sprintf("uid#%d",$<); +@files = process_argv(@ARGV); +@path = split('/', $files[0]); +$repository = $path[0]; +if ($#path == 0) { + $dir = "."; +} else { + $dir = join('/', @path[1..$#path]); +} +#print("ARGV - ", join(":", @ARGV), "\n"); +#print("files - ", join(":", @files), "\n"); +#print("path - ", join(":", @path), "\n"); +#print("dir - ", $dir, "\n"); +#print("id - ", $id, "\n"); + +# +# Map the repository directory to an email address for commitlogs to be sent +# to. +# +#$mlist = &mlist_map($files[0]); + +########################## +# +# Check for a new directory first. This will always appear as a +# single item in the argument list, and an empty log message. +# +if ($ARGV[0] =~ /New directory/) { + $header = &build_header; + @text = (); + push(@text, $header); + push(@text, ""); + push(@text, " ".$ARGV[0]); + &mail_notification([ $mlist ], @text); + exit 0; +} + +# +# Iterate over the body of the message collecting information. +# +while () { + chomp; # Drop the newline + if (/^Revision\/Branch:/) { + s,^Revision/Branch:,,; + push (@branch_lines, split); + next; + } +# next if (/^[ \t]+Tag:/ && $state != $STATE_LOG); + if (/^Modified Files/) { $state = $STATE_CHANGED; next; } + if (/^Added Files/) { $state = $STATE_ADDED; next; } + if (/^Removed Files/) { $state = $STATE_REMOVED; next; } + if (/^Log Message/) { $state = $STATE_LOG; next; } + s/[ \t\n]+$//; # delete trailing space + + push (@changed_files, split) if ($state == $STATE_CHANGED); + push (@added_files, split) if ($state == $STATE_ADDED); + push (@removed_files, split) if ($state == $STATE_REMOVED); + if ($state == $STATE_LOG) { + if (/^PR:$/i || + /^Reviewed by:$/i || + /^Submitted by:$/i || + /^Obtained from:$/i) { + next; + } + push (@log_lines, $_); + } +} + +# +# Strip leading and trailing blank lines from the log message. Also +# compress multiple blank lines in the body of the message down to a +# single blank line. +# (Note, this only does the mail and changes log, not the rcs log). +# +while ($#log_lines > -1) { + last if ($log_lines[0] ne ""); + shift(@log_lines); +} +while ($#log_lines > -1) { + last if ($log_lines[$#log_lines] ne ""); + pop(@log_lines); +} +for ($i = $#log_lines; $i > 0; $i--) { + if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) { + splice(@log_lines, $i, 1); + } +} + +# +# Find the log file that matches this log message +# +for ($i = 0; ; $i++) { + last if (! -e "$LOG_FILE.$i.$id.$cvs_user"); + @text = &read_logfile("$LOG_FILE.$i.$id.$cvs_user", ""); + last if ($#text == -1); + last if (join(" ", @log_lines) eq join(" ", @text)); +} + +# +# Spit out the information gathered in this pass. +# +&write_logfile("$LOG_FILE.$i.$id.$cvs_user", @log_lines); +&append_to_file("$BRANCH_FILE.$i.$id.$cvs_user", $dir, @branch_lines); +&append_to_file("$ADDED_FILE.$i.$id.$cvs_user", $dir, @added_files); +&append_to_file("$CHANGED_FILE.$i.$id.$cvs_user", $dir, @changed_files); +&append_to_file("$REMOVED_FILE.$i.$id.$cvs_user", $dir, @removed_files); +&append_line("$MLIST_FILE.$i.$id.$cvs_user", $mlist); +if ($rcsidinfo) { + &change_summary("$SUMMARY_FILE.$i.$id.$cvs_user", (@changed_files, @added_files)); +} + +# +# Check whether this is the last directory. If not, quit. +# +if (-e "$LAST_FILE.$id.$cvs_user") { + $_ = &read_line("$LAST_FILE.$id.$cvs_user"); + $tmpfiles = $files[0]; + $tmpfiles =~ s,([^a-zA-Z0-9_/]),\\$1,g; + if (! grep(/$tmpfiles$/, $_)) { + print "More commits to come...\n"; + exit 0 + } +} + +# +# This is it. The commits are all finished. Lump everything together +# into a single message, fire a copy off to the mailing list, and drop +# it on the end of the Changes file. +# +$header = &build_header; + +# +# Produce the final compilation of the log messages +# +@text = (); +@mlist_list = (); +push(@text, $header); +push(@text, ""); +for ($i = 0; ; $i++) { + last if (! -e "$LOG_FILE.$i.$id.$cvs_user"); + push(@text, &read_file("$BRANCH_FILE.$i.$id.$cvs_user", "Branch:")); + push(@text, &read_file("$CHANGED_FILE.$i.$id.$cvs_user", "Modified:")); + push(@text, &read_file("$ADDED_FILE.$i.$id.$cvs_user", "Added:")); + push(@text, &read_file("$REMOVED_FILE.$i.$id.$cvs_user", "Removed:")); + push(@text, " Log:"); + push(@text, &read_logfile("$LOG_FILE.$i.$id.$cvs_user", " ")); + push(@mlist_list, &read_file_lines("$MLIST_FILE.$i.$id.$cvs_user")); + if ($rcsidinfo == 2) { + if (-e "$SUMMARY_FILE.$i.$id.$cvs_user") { + push(@text, " "); + push(@text, " Revision Changes Path"); + push(@text, &read_logfile("$SUMMARY_FILE.$i.$id.$cvs_user", " ")); + } + } + push(@text, ""); +} + +# +# Now generate the extra info for the mail message.. +# +if ($rcsidinfo == 1) { + $revhdr = 0; + for ($i = 0; ; $i++) { + last if (! -e "$LOG_FILE.$i.$id.$cvs_user"); + if (-e "$SUMMARY_FILE.$i.$id.$cvs_user") { + if (!$revhdr++) { + push(@text, "Revision Changes Path"); + } + push(@text, &read_logfile("$SUMMARY_FILE.$i.$id.$cvs_user", "")); + } + } + if ($revhdr) { + push(@text, ""); # consistancy... + } +} + +%mlist_hash = (); + +foreach (@mlist_list) { $mlist_hash{ $_ } = 1; } + +# +# Mail out the notification. +# +&mail_notification([ keys(%mlist_hash) ], @text); +&cleanup_tmpfiles; +exit 0; diff --git a/contrib/cvs-1.12.9/contrib/mfpipe.in b/contrib/cvs-1.12.9/contrib/mfpipe.in new file mode 100644 index 0000000000..1ecceb0690 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/mfpipe.in @@ -0,0 +1,85 @@ +#! @PERL@ +# -*-Perl-*- +# +# From: clyne@niwot.scd.ucar.EDU (John Clyne) +# Date: Fri, 28 Feb 92 09:54:21 MST +# +# BTW, i wrote a perl script that is similar to 'nfpipe' except that in +# addition to logging to a file it provides a command line option for mailing +# change notices to a group of users. Obviously you probably wouldn't want +# to mail every change. But there may be certain directories that are commonly +# accessed by a group of users who would benefit from an email notice. +# Especially if they regularly beat on the same directory. Anyway if you +# think anyone would be interested here it is. +# +# File: mfpipe +# +# Author: John Clyne +# National Center for Atmospheric Research +# PO 3000, Boulder, Colorado +# +# Date: Wed Feb 26 18:34:53 MST 1992 +# +# Description: Tee standard input to mail a list of users and to +# a file. Used by CVS logging. +# +# Usage: mfpipe [-f file] [user@host...] +# +# Environment: CVSROOT +# Path to CVS root. +# +# Files: +# +# +# Options: -f file +# Capture output to 'file' +# + +$header = "Log Message:\n"; + +$mailcmd = "| mail -s 'CVS update notice'"; +$whoami = `whoami`; +chop $whoami; +$date = `date`; +chop $date; + +$cvsroot = $ENV{'CVSROOT'}; + +while (@ARGV) { + $arg = shift @ARGV; + + if ($arg eq '-f') { + $file = shift @ARGV; + } + else { + $users = "$users $arg"; + } +} + +if ($users) { + $mailcmd = "$mailcmd $users"; + open(MAIL, $mailcmd) || die "Execing $mail: $!\n"; +} + +if ($file) { + $logfile = "$cvsroot/LOG/$file"; + open(FILE, ">> $logfile") || die "Opening $logfile: $!\n"; +} + +print FILE "$whoami $date--------BEGIN LOG ENTRY-------------\n" if ($logfile); + +while (<>) { + print FILE $log if ($log && $logfile); + + print FILE $_ if ($logfile); + print MAIL $_ if ($users); + + $log = "log: " if ($_ eq $header); +} + +close FILE; +die "Write failed" if $?; +close MAIL; +die "Mail failed" if $?; + +exit 0; diff --git a/contrib/cvs-1.12.9/contrib/rcs-to-cvs.sh b/contrib/cvs-1.12.9/contrib/rcs-to-cvs.sh new file mode 100644 index 0000000000..66a62a9da5 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/rcs-to-cvs.sh @@ -0,0 +1,184 @@ +#! /bin/sh +# +# Based on the CVS 1.0 checkin csh script. +# Contributed by Per Cederqvist . +# Rewritten in sh by David MacKenzie . +# +# Copyright (c) 1989, Brian Berliner +# +# You may distribute under the terms of the GNU General Public License. +# +############################################################################# +# +# Check in sources that previously were under RCS or no source control system. +# +# The repository is the directory where the sources should be deposited. +# +# Traverses the current directory, ensuring that an +# identical directory structure exists in the repository directory. It +# then checks the files in in the following manner: +# +# 1) If the file doesn't yet exist, check it in as revision 1.1 +# +# The script also is somewhat verbose in letting the user know what is +# going on. It prints a diagnostic when it creates a new file, or updates +# a file that has been modified on the trunk. +# +# Bugs: doesn't put the files in branch 1.1.1 +# doesn't put in release and vendor tags +# +############################################################################# + +usage="Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository" +vbose=0 +message="" +if [ -d /var/tmp ]; then message_file=/var/tmp/checkin.$$; else message_file=/usr/tmp/checkin.$$; fi +got_one=0 + +if [ $# -lt 1 ]; then + echo "$usage" >&2 + exit 1 +fi + +while [ $# -ne 0 ]; do + case "$1" in + -v) + vbose=1 + ;; + -m) + shift + echo $1 > $message_file + got_one=1 + ;; + -f) + shift + message_file=$1 + got_one=2 + ;; + *) + break + esac + shift +done + +if [ $# -lt 1 ]; then + echo "$usage" >&2 + exit 1 +fi + +repository=$1 +shift + +if [ -z "$CVSROOT" ]; then + echo "Please the environmental variable CVSROOT to the root" >&2 + echo " of the tree you wish to update" >&2 + exit 1 +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-vi} $message_file + got_one=1 +fi + +# Ya gotta share. +umask 0 + +update_dir=${CVSROOT}/${repository} +[ ! -d ${update_dir} ] && mkdir $update_dir + +if [ -d SCCS ]; then + echo SCCS files detected! >&2 + exit 1 +fi +if [ -d RCS ]; then + co RCS/* +fi + +for name in * .[a-zA-Z0-9]* +do + case "$name" in + RCS | *~ | \* | .\[a-zA-Z0-9\]\* ) continue ;; + esac + echo $name + if [ $vbose -ne 0 ]; then + echo "Updating ${repository}/${name}" + fi + if [ -d "$name" ]; then + if [ ! -d "${update_dir}/${name}" ]; then + echo "WARNING: Creating new directory ${repository}/${name}" + mkdir "${update_dir}/${name}" + if [ $? -ne 0 ]; then + echo "ERROR: mkdir failed - aborting" >&2 + exit 1 + fi + fi + cd "$name" + if [ $? -ne 0 ]; then + echo "ERROR: Couldn\'t cd to $name - aborting" >&2 + exit 1 + fi + if [ $vbose -ne 0 ]; then + $0 -v -f $message_file "${repository}/${name}" + else + $0 -f $message_file "${repository}/${name}" + fi + if [ $? -ne 0 ]; then + exit 1 + fi + cd .. + else # if not directory + if [ ! -f "$name" ]; then + echo "WARNING: $name is neither a regular file" + echo " nor a directory - ignored" + continue + fi + file="${update_dir}/${name},v" + comment="" + if grep -s '\$Log.*\$' "${name}"; then # If $Log keyword + myext=`echo $name | sed 's,.*\.,,'` + [ "$myext" = "$name" ] && myext= + case "$myext" in + c | csh | e | f | h | l | mac | me | mm | ms | p | r | red | s | sh | sl | cl | ml | el | tex | y | ye | yr | "" ) + ;; + + * ) + echo "For file ${file}:" + grep '\$Log.*\$' "${name}" + echo -n "Please insert a comment leader for file ${name} > " + read comment + ;; + esac + fi + if [ ! -f "$file" ]; then # If not exists in repository + if [ ! -f "${update_dir}/Attic/${name},v" ]; then + echo "WARNING: Creating new file ${repository}/${name}" + if [ -f RCS/"${name}",v ]; then + echo "MSG: Copying old rcs file." + cp RCS/"${name}",v "$file" + else + if [ -n "${comment}" ]; then + rcs -q -i -c"${comment}" -t${message_file} -m'.' "$file" + fi + ci -q -u1.1 -t${message_file} -m'.' "$file" + if [ $? -ne 0 ]; then + echo "ERROR: Initial check-in of $file failed - aborting" >&2 + exit 1 + fi + fi + else + file="${update_dir}/Attic/${name},v" + echo "WARNING: IGNORED: ${repository}/Attic/${name}" + continue + fi + else # File existed + echo "ERROR: File exists in repository: Ignored: $file" + continue + fi + fi +done + +[ $got_one -eq 1 ] && rm -f $message_file + +exit 0 diff --git a/contrib/cvs-1.12.9/contrib/rcs2log.sh b/contrib/cvs-1.12.9/contrib/rcs2log.sh new file mode 100644 index 0000000000..d01b8abca1 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/rcs2log.sh @@ -0,0 +1,730 @@ +#! /bin/sh + +# RCS to ChangeLog generator + +# Generate a change log prefix from RCS files (perhaps in the CVS repository) +# and the ChangeLog (if any). +# Output the new prefix to standard output. +# You can edit this prefix by hand, and then prepend it to ChangeLog. + +# Ignore log entries that start with `#'. +# Clump together log entries that start with `{topic} ', +# where `topic' contains neither white space nor `}'. + +Help='The default FILEs are the files registered under the working directory. +Options: + + -c CHANGELOG Output a change log prefix to CHANGELOG (default ChangeLog). + -h HOSTNAME Use HOSTNAME in change log entries (default current host). + -i INDENT Indent change log lines by INDENT spaces (default 8). + -l LENGTH Try to limit log lines to LENGTH characters (default 79). + -L FILE Use rlog-format FILE for source of logs. + -R If no FILEs are given and RCS is used, recurse through working directory. + -r OPTION Pass OPTION to subsidiary log command. + -t TABWIDTH Tab stops are every TABWIDTH characters (default 8). + -u "LOGINFULLNAMEMAILADDR" Assume LOGIN has FULLNAME and MAILADDR. + -v Append RCS revision to file names in log lines. + --help Output help. + --version Output version number. + +Report bugs to .' + +Id='$Id: rcs2log,v 1.48 2001/09/05 23:07:46 eggert Exp $' + +# Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2001, 2003 +# Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +Copyright='Copyright 1992-2003 Free Software Foundation, Inc. +This program comes with NO WARRANTY, to the extent permitted by law. +You may redistribute copies of this program +under the terms of the GNU General Public License. +For more information about these matters, see the files named COPYING. +Author: Paul Eggert ' + +# functions +@MKTEMP_SH_FUNCTION@ + +# Use the traditional C locale. +LANG=C +LANGUAGE=C +LC_ALL=C +LC_COLLATE=C +LC_CTYPE=C +LC_MESSAGES=C +LC_NUMERIC=C +LC_TIME=C +export LANG LANGUAGE LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES LC_NUMERIC LC_TIME + +# These variables each contain a single ASCII character. +# Unfortunately, there's no portable way of writing these characters +# in older Unix implementations, other than putting them directly into +# this text file. +SOH='' # SOH, octal code 001 +tab=' ' +nl=' +' + +# Parse options. + +# defaults +: ${MKTEMP="@MKTEMP@"} +: ${AWK=awk} +: ${TMPDIR=/tmp} + +changelog=ChangeLog # change log file name +datearg= # rlog date option +hostname= # name of local host (if empty, will deduce it later) +indent=8 # indent of log line +length=79 # suggested max width of log line +logins= # login names for people we know fullnames and mailaddrs of +loginFullnameMailaddrs= # loginfullnamemailaddr triplets +logTZ= # time zone for log dates (if empty, use local time) +recursive= # t if we want recursive rlog +revision= # t if we want revision numbers +rlog_options= # options to pass to rlog +rlogfile= # log file to read from +tabwidth=8 # width of horizontal tab + +while : +do + case $1 in + -c) changelog=${2?}; shift;; + -i) indent=${2?}; shift;; + -h) hostname=${2?}; shift;; + -l) length=${2?}; shift;; + -L) rlogfile=${2?}; shift;; + -[nu]) # -n is obsolescent; it is replaced by -u. + case $1 in + -n) case ${2?}${3?}${4?} in + *"$tab"* | *"$nl"*) + echo >&2 "$0: -n '$2' '$3' '$4': tabs, newlines not allowed" + exit 1;; + esac + login=$2 + lfm=$2$tab$3$tab$4 + shift; shift; shift;; + -u) + # If $2 is not tab-separated, use colon for separator. + case ${2?} in + *"$nl"*) + echo >&2 "$0: -u '$2': newlines not allowed" + exit 1;; + *"$tab"*) + t=$tab;; + *) + t=':';; + esac + case $2 in + *"$t"*"$t"*"$t"*) + echo >&2 "$0: -u '$2': too many fields" + exit 1;; + *"$t"*"$t"*) + uf="[^$t]*$t" # An unselected field, followed by a separator. + sf="\\([^$t]*\\)" # The selected field. + login=`expr "X$2" : "X$sf"` + lfm="$login$tab"` + expr "X$2" : "$uf$sf" + `"$tab"` + expr "X$2" : "$uf$uf$sf" + `;; + *) + echo >&2 "$0: -u '$2': not enough fields" + exit 1;; + esac + shift;; + esac + case $logins in + '') logins=$login;; + ?*) logins=$logins$nl$login;; + esac + case $loginFullnameMailaddrs in + '') loginFullnameMailaddrs=$lfm;; + ?*) loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$lfm;; + esac;; + -r) + case $rlog_options in + '') rlog_options=${2?};; + ?*) rlog_options=$rlog_options$nl${2?};; + esac + shift;; + -R) recursive=t;; + -t) tabwidth=${2?}; shift;; + -v) revision=t;; + --version) + set $Id + rcs2logVersion=$3 + echo >&2 "rcs2log (GNU Emacs) $rcs2logVersion$nl$Copyright" + exit 0;; + -*) echo >&2 "Usage: $0 [OPTION]... [FILE ...]$nl$Help" + case $1 in + --help) exit 0;; + *) exit 1;; + esac;; + *) break;; + esac + shift +done + +month_data=' + m[0]="Jan"; m[1]="Feb"; m[2]="Mar" + m[3]="Apr"; m[4]="May"; m[5]="Jun" + m[6]="Jul"; m[7]="Aug"; m[8]="Sep" + m[9]="Oct"; m[10]="Nov"; m[11]="Dec" +' + +logdir=`$MKTEMP -d $TMPDIR/rcs2log.XXXXXX` +test -n "$logdir" || exit +llogout=$logdir/l +trap exit 1 2 13 15 +trap "rm -fr $logdir 2>/dev/null" 0 + +# If no rlog-format log file is given, generate one into $rlogfile. +case $rlogfile in +'') + rlogfile=$logdir/r + + # If no rlog options are given, + # log the revisions checked in since the first ChangeLog entry. + # Since ChangeLog is only by date, some of these revisions may be duplicates of + # what's already in ChangeLog; it's the user's responsibility to remove them. + case $rlog_options in + '') + if test -s "$changelog" + then + e=' + /^[0-9]+-[0-9][0-9]-[0-9][0-9]/{ + # ISO 8601 date + print $1 + exit + } + /^... ... [ 0-9][0-9] [ 0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9]+ /{ + # old-fashioned date and time (Emacs 19.31 and earlier) + '"$month_data"' + year = $5 + for (i=0; i<=11; i++) if (m[i] == $2) break + dd = $3 + printf "%d-%02d-%02d\n", year, i+1, dd + exit + } + ' + d=`$AWK "$e" <"$changelog"` || exit + case $d in + ?*) datearg="-d>$d";; + esac + fi;; + esac + + # Use TZ specified by ChangeLog local variable, if any. + if test -s "$changelog" + then + extractTZ=' + /^.*change-log-time-zone-rule['"$tab"' ]*:['"$tab"' ]*"\([^"]*\)".*/{ + s//\1/; p; q + } + /^.*change-log-time-zone-rule['"$tab"' ]*:['"$tab"' ]*t.*/{ + s//UTC0/; p; q + } + ' + logTZ=`tail "$changelog" | sed -n "$extractTZ"` + case $logTZ in + ?*) TZ=$logTZ; export TZ;; + esac + fi + + # If CVS is in use, examine its repository, not the normal RCS files. + if test ! -f CVS/Repository + then + rlog=rlog + repository= + else + rlog='cvs -q log' + repository=`sed 1q &2 "$0: $CVSROOT: CVSROOT has multiple ':/'s" + exit 1;; + *:/*) + # remote repository + pository=`expr "X$repository" : '.*:\(/.*\)'`;; + *) + # local repository + case $repository in + /*) ;; + *) repository=${CVSROOT?}/$repository;; + esac + if test ! -d "$repository" + then + echo >&2 "$0: $repository: bad repository (see CVS/Repository)" + exit 1 + fi + pository=$repository;; + esac + + # Ensure that $pository ends in exactly one slash. + while : + do + case $pository in + *//) pository=`expr "X$pository" : 'X\(.*\)/'`;; + */) break;; + *) pository=$pository/; break;; + esac + done + + fi + + # Use $rlog's -zLT option, if $rlog supports it. + case `$rlog -zLT 2>&1` in + *' option'*) ;; + *) + case $rlog_options in + '') rlog_options=-zLT;; + ?*) rlog_options=-zLT$nl$rlog_options;; + esac;; + esac + + # With no arguments, examine all files under the RCS directory. + case $# in + 0) + case $repository in + '') + oldIFS=$IFS + IFS=$nl + case $recursive in + t) + RCSdirs=`find . -name RCS -type d -print` + filesFromRCSfiles='s|,v$||; s|/RCS/|/|; s|^\./||' + files=` + { + case $RCSdirs in + ?*) find $RCSdirs \ + -type f \ + ! -name '*_' \ + ! -name ',*,' \ + ! -name '.*_' \ + ! -name .rcsfreeze.log \ + ! -name .rcsfreeze.ver \ + -print;; + esac + find . -name '*,v' -print + } | + sort -u | + sed "$filesFromRCSfiles" + `;; + *) + files= + for file in RCS/.* RCS/* .*,v *,v + do + case $file in + RCS/. | RCS/.. | RCS/,*, | RCS/*_) continue;; + RCS/.rcsfreeze.log | RCS/.rcsfreeze.ver) continue;; + RCS/.\* | RCS/\* | .\*,v | \*,v) test -f "$file" || continue;; + RCS/*,v | RCS/.*,v) ;; + RCS/* | RCS/.*) test -f "$file" || continue;; + esac + case $files in + '') files=$file;; + ?*) files=$files$nl$file;; + esac + done + case $files in + '') exit 0;; + esac;; + esac + set x $files + shift + IFS=$oldIFS;; + esac;; + esac + + case $datearg in + ?*) $rlog $rlog_options "$datearg" ${1+"$@"} >$rlogfile;; + '') $rlog $rlog_options ${1+"$@"} >$rlogfile;; + esac || exit;; +esac + + +# Get the full name of each author the logs mention, and set initialize_fullname +# to awk code that initializes the `fullname' awk associative array. +# Warning: foreign authors (i.e. not known in the passwd file) are mishandled; +# you have to fix the resulting output by hand. + +initialize_fullname= +initialize_mailaddr= + +case $loginFullnameMailaddrs in +?*) + case $loginFullnameMailaddrs in + *\"* | *\\*) + sed 's/["\\]/\\&/g' >$llogout <$llogout || exit + +output_authors='/^date: / { + if ($2 ~ /^[0-9]*[-\/][0-9][0-9][-\/][0-9][0-9]$/ && $3 ~ /^[0-9][0-9]:[0-9][0-9]:[0-9][0-9][-+0-9:]*;$/ && $4 == "author:" && $5 ~ /^[^;]*;$/) { + print substr($5, 1, length($5)-1) + } +}' +authors=` + $AWK "$output_authors" <"$rlogfile" | sort -u | comm -23 - $llogout +` +case $authors in +?*) + cat >$llogout </dev/null | + $AWK -F: "$awkscript" + `$initialize_fullname;; +esac + + +# Function to print a single log line. +# We don't use awk functions, to stay compatible with old awk versions. +# `Log' is the log message. +# `files' contains the affected files. +printlogline='{ + + # Following the GNU coding standards, rewrite + # * file: (function): comment + # to + # * file (function): comment + if (Log ~ /^\([^)]*\): /) { + i = index(Log, ")") + filefunc = substr(Log, 1, i) + while ((j = index(filefunc, "\n"))) { + files = files " " substr(filefunc, 1, j-1) + filefunc = substr(filefunc, j+1) + } + files = files " " filefunc + Log = substr(Log, i+3) + } + + # If "label: comment" is too long, break the line after the ":". + sep = " " + i = index(Log, "\n") + if ('"$length"' <= '"$indent"' + 1 + length(files) + i) sep = "\n" indent_string + + # Print the label. + printf "%s*%s:", indent_string, files + + # Print each line of the log. + while (i) { + logline = substr(Log, 1, i-1) + if (logline ~ /[^'"$tab"' ]/) { + printf "%s%s\n", sep, logline + } else { + print "" + } + sep = indent_string + Log = substr(Log, i+1) + i = index(Log, "\n") + } +}' + +# Pattern to match the `revision' line of rlog output. +rlog_revision_pattern='^revision [0-9]+\.[0-9]+(\.[0-9]+\.[0-9]+)*(['"$tab"' ]+locked by: [^'"$tab"' $,.0-9:;@]*[^'"$tab"' $,:;@][^'"$tab"' $,.0-9:;@]*;)?['"$tab"' ]*$' + +case $hostname in +'') + hostname=`( + hostname || uname -n || uuname -l || cat /etc/whoami + ) 2>/dev/null` || { + 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 + + +# Process the rlog output, generating ChangeLog style entries. + +# First, reformat the rlog output so that each line contains one log entry. +# Transliterate \n to SOH so that multiline entries fit on a single line. +# Discard irrelevant rlog output. +$AWK ' + BEGIN { + pository = "'"$pository"'" + SOH="'"$SOH"'" + } + /^RCS file: / { + if (pository != "") { + filename = substr($0, 11) + if (substr(filename, 1, length(pository)) == pository) { + filename = substr(filename, length(pository) + 1) + } + if (filename ~ /,v$/) { + filename = substr(filename, 1, length(filename) - 2) + } + if (filename ~ /(^|\/)Attic\/[^\/]*$/) { + i = length(filename) + while (substr(filename, i, 1) != "/") i-- + filename = substr(filename, 1, i - 6) substr(filename, i + 1) + } + } + rev = "?" + } + /^Working file: / { if (repository == "") filename = substr($0, 15) } + /'"$rlog_revision_pattern"'/, /^(-----------*|===========*)$/ { + line = $0 + if (line ~ /'"$rlog_revision_pattern"'/) { + rev = $2 + next + } + if (line ~ /^date: [0-9][- +\/0-9:]*;/) { + date = $2 + if (date ~ /\//) { + # This is a traditional RCS format date YYYY/MM/DD. + # Replace "/"s with "-"s to get ISO format. + newdate = "" + while ((i = index(date, "/")) != 0) { + newdate = newdate substr(date, 1, i-1) "-" + date = substr(date, i+1) + } + date = newdate date + } + time = substr($3, 1, length($3) - 1) + author = substr($5, 1, length($5)-1) + printf "%s%s%s%s%s%s%s%s%s%s", filename, SOH, rev, SOH, date, SOH, time, SOH, author, SOH + rev = "?" + next + } + if (line ~ /^branches: /) { next } + if (line ~ /^(-----------*|===========*)$/) { print ""; next } + if (line == "Initial revision" || line ~ /^file .+ was initially added on branch .+\.$/) { + line = "New file." + } + printf "%s%s", line, SOH + } +' <"$rlogfile" | + +# Now each line is of the form +# FILENAME@REVISION@YYYY-MM-DD@HH:MM:SS[+-TIMEZONE]@AUTHOR@LOG +# where @ stands for an SOH (octal code 001), +# and each line of LOG is terminated by SOH instead of \n. +# Sort the log entries, first by date+time (in reverse order), +# then by author, then by log entry, and finally by file name and revision +# (just in case). +sort -t"$SOH" +2 -4r +4 +0 | + +# Finally, reformat the sorted log entries. +$AWK -F"$SOH" ' + BEGIN { + logTZ = "'"$logTZ"'" + revision = "'"$revision"'" + + # Initialize the fullname and mailaddr associative arrays. + '"$initialize_fullname"' + '"$initialize_mailaddr"' + + # Initialize indent string. + indent_string = "" + i = '"$indent"' + if (0 < '"$tabwidth"') + for (; '"$tabwidth"' <= i; i -= '"$tabwidth"') + indent_string = indent_string "\t" + while (1 <= i--) + indent_string = indent_string " " + } + + { + newlog = "" + for (i = 6; i < NF; i++) newlog = newlog $i "\n" + + # Ignore log entries prefixed by "#". + if (newlog ~ /^#/) { next } + + if (Log != newlog || date != $3 || author != $5) { + + # The previous log and this log differ. + + # Print the old log. + if (date != "") '"$printlogline"' + + # Logs that begin with "{clumpname} " should be grouped together, + # and the clumpname should be removed. + # Extract the new clumpname from the log header, + # and use it to decide whether to output a blank line. + newclumpname = "" + sep = "\n" + if (date == "") sep = "" + if (newlog ~ /^\{[^'"$tab"' }]*}['"$tab"' ]/) { + i = index(newlog, "}") + newclumpname = substr(newlog, 1, i) + while (substr(newlog, i+1) ~ /^['"$tab"' ]/) i++ + newlog = substr(newlog, i+1) + if (clumpname == newclumpname) sep = "" + } + printf sep + clumpname = newclumpname + + # Get ready for the next log. + Log = newlog + if (files != "") + for (i in filesknown) + filesknown[i] = 0 + files = "" + } + if (date != $3 || author != $5) { + # The previous date+author and this date+author differ. + # Print the new one. + date = $3 + time = $4 + author = $5 + + zone = "" + if (logTZ && ((i = index(time, "-")) || (i = index(time, "+")))) + zone = " " substr(time, i) + + # Print "date[ timezone] fullname ". + # Get fullname and email address from associative arrays; + # default to author and author@hostname if not in arrays. + if (fullname[author]) + auth = fullname[author] + else + auth = author + printf "%s%s %s ", date, zone, auth + if (mailaddr[author]) + printf "<%s>\n\n", mailaddr[author] + else + printf "<%s@%s>\n\n", author, "'"$hostname"'" + } + if (! filesknown[$1]) { + filesknown[$1] = 1 + if (files == "") files = " " $1 + else files = files ", " $1 + if (revision && $2 != "?") files = files " " $2 + } + } + END { + # Print the last log. + if (date != "") { + '"$printlogline"' + printf "\n" + } + } +' && + + +# Exit successfully. + +exec rm -fr $logdir + +# Local Variables: +# tab-width:4 +# End: diff --git a/contrib/cvs-1.12.9/contrib/rcslock.in b/contrib/cvs-1.12.9/contrib/rcslock.in new file mode 100644 index 0000000000..e17356b9b1 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/rcslock.in @@ -0,0 +1,235 @@ +#! @PERL@ +# -*-Perl-*- + +# Author: John Rouillard (rouilj@cs.umb.edu) +# Supported: Yeah right. (Well what do you expect for 2 hours work?) +# Blame-to: rouilj@cs.umb.edu +# Complaints to: Anybody except Brian Berliner, he's blameless for +# this script. +# Acknowlegements: The base code for this script has been acquired +# from the log.pl script. + +# rcslock.pl - A program to prevent commits when a file to be ckecked +# in is locked in the repository. + +# There are times when you need exclusive access to a file. This +# often occurs when binaries are checked into the repository, since +# cvs's (actually rcs's) text based merging mechanism won't work. This +# script allows you to use the rcs lock mechanism (rcs -l) to make +# sure that no changes to a repository are able to be committed if +# those changes would result in a locked file being changed. + +# WARNING: +# This script will work only if locking is set to strict. +# + +# Setup: +# Add the following line to the commitinfo file: + +# ALL /local/location/for/script/lockcheck [options] + +# Where ALL is replaced by any suitable regular expression. +# Options are -v for verbose info, or -d for debugging info. +# The %s will provide the repository directory name and the names of +# all changed files. + +# Use: +# When a developer needs exclusive access to a version of a file, s/he +# should use "rcs -l" in the repository tree to lock the version they +# are working on. CVS will automagically release the lock when the +# commit is performed. + +# Method: +# An "rlog -h" is exec'ed to give info on all about to be +# committed files. This (header) information is parsed to determine +# if any locks are outstanding and what versions of the file are +# locked. This filename, version number info is used to index an +# associative array. All of the files to be committed are checked to +# see if any locks are outstanding. If locks are outstanding, the +# version number of the current file (taken from the CVS/Entries +# subdirectory) is used in the key to determine if that version is +# locked. If the file being checked in is locked by the person doing +# the checkin, the commit is allowed, but if the lock is held on that +# version of a file by another person, the commit is not allowed. + +$ext = ",v"; # The extension on your rcs files. + +$\="\n"; # I hate having to put \n's at the end of my print statements +$,=' '; # Spaces should occur between arguments to print when printed + +# turn off setgid +# +$) = $(; + +# +# parse command line arguments +# +require 'getopts.pl'; + +&Getopts("vd"); # verbose or debugging + +# Verbose is useful when debugging +$opt_v = $opt_d if defined $opt_d; + +# $files[0] is really the name of the subdirectory. +# @files = split(/ /,$ARGV[0]); +@files = @ARGV[0..$#ARGV]; +$cvsroot = $ENV{'CVSROOT'}; + +# +# get login name +# +$login = getlogin || (getpwuid($<))[0] || "nobody"; + +# +# save the current directory since we have to return here to parse the +# CVS/Entries file if a lock is found. +# +$pwd = `/bin/pwd`; +chop $pwd; + +print "Starting directory is $pwd" if defined $opt_d ; + +# +# cd to the repository directory and check on the files. +# +print "Checking directory ", $files[0] if defined $opt_v ; + +if ( $files[0] =~ /^\// ) +{ + print "Directory path is $files[0]" if defined $opt_d ; + chdir $files[0] || die "Can't change to repository directory $files[0]" ; +} +else +{ + print "Directory path is $cvsroot/$files[0]" if defined $opt_d ; + chdir ($cvsroot . "/" . $files[0]) || + die "Can't change to repository directory $files[0] in $cvsroot" ; +} + + +# Open the rlog process and apss all of the file names to that one +# process to cut down on exec overhead. This may backfire if there +# are too many files for the system buffer to handle, but if there are +# that many files, chances are that the cvs repository is not set up +# cleanly. + +print "opening rlog -h @files[1..$#files] |" if defined $opt_d; + +open( RLOG, "rlog -h @files[1..$#files] |") || die "Can't run rlog command" ; + +# Create the locks associative array. The elements in the array are +# of two types: +# +# The name of the RCS file with a value of the total number of locks found +# for that file, +# or +# +# The name of the rcs file concatenated with the version number of the lock. +# The value of this element is the name of the locker. + +# The regular expressions used to split the rcs info may have to be changed. +# The current ones work for rcs 5.6. + +$lock = 0; + +while () +{ + chop; + next if /^$/; # ditch blank lines + + if ( $_ =~ /^RCS file: (.*)$/ ) + { + $curfile = $1; + next; + } + + if ( $_ =~ /^locks: strict$/ ) + { + $lock = 1 ; + next; + } + + if ( $lock ) + { + # access list: is the line immediately following the list of locks. + if ( /^access list:/ ) + { # we are done getting lock info for this file. + $lock = 0; + } + else + { # We are accumulating lock info. + + # increment the lock count + $locks{$curfile}++; + # save the info on the version that is locked. $2 is the + # version number $1 is the name of the locker. + $locks{"$curfile" . "$2"} = $1 + if /[ ]*([a-zA-Z._]*): ([0-9.]*)$/; + + print "lock by $1 found on $curfile version $2" if defined $opt_d; + + } + } +} + +# Lets go back to the starting directory and see if any locked files +# are ones we are interested in. + +chdir $pwd; + +# fo all of the file names (remember $files[0] is the directory name +foreach $i (@files[1..$#files]) +{ + if ( defined $locks{$i . $ext} ) + { # well the file has at least one lock outstanding + + # find the base version number of our file + &parse_cvs_entry($i,*entry); + + # is our version of this file locked? + if ( defined $locks{$i . $ext . $entry{"version"}} ) + { # if so, it is by us? + if ( $login ne ($by = $locks{$i . $ext . $entry{"version"}}) ) + {# crud somebody else has it locked. + $outstanding_lock++ ; + print "$by has file $i locked for version " , $entry{"version"}; + } + else + { # yeah I have it locked. + print "You have a lock on file $i for version " , $entry{"version"} + if defined $opt_v; + } + } + } +} + +exit $outstanding_lock; + + +### End of main program + +sub parse_cvs_entry +{ # a very simple minded hack at parsing an entries file. +local ( $file, *entry ) = @_; +local ( @pp ); + + +open(ENTRIES, "< CVS/Entries") || die "Can't open entries file"; + +while () + { + if ( $_ =~ /^\/$file\// ) + { + @pp = split('/'); + + $entry{"name"} = $pp[1]; + $entry{"version"} = $pp[2]; + $entry{"dates"} = $pp[3]; + $entry{"name"} = $pp[4]; + $entry{"name"} = $pp[5]; + $entry{"sticky"} = $pp[6]; + return; + } + } +} diff --git a/contrib/cvs-1.12.9/contrib/sccs2rcs.in b/contrib/cvs-1.12.9/contrib/sccs2rcs.in new file mode 100644 index 0000000000..cc234973e7 --- /dev/null +++ b/contrib/cvs-1.12.9/contrib/sccs2rcs.in @@ -0,0 +1,314 @@ +#! @CSH@ -f +# +# Sccs2rcs is a script to convert an existing SCCS +# history into an RCS history without losing any of +# the information contained therein. +# It has been tested under the following OS's: +# SunOS 3.5, 4.0.3, 4.1 +# Ultrix-32 2.0, 3.1 +# +# Things to note: +# + It will NOT delete or alter your ./SCCS history under any circumstances. +# +# + Run in a directory where ./SCCS exists and where you can +# create ./RCS +# +# + /usr/local/bin is put in front of the default path. +# (SCCS under Ultrix is set-uid sccs, bad bad bad, so +# /usr/local/bin/sccs here fixes that) +# +# + Date, time, author, comments, branches, are all preserved. +# +# + If a command fails somewhere in the middle, it bombs with +# a message -- remove what it's done so far and try again. +# "rm -rf RCS; sccs unedit `sccs tell`; sccs clean" +# There is no recovery and exit is far from graceful. +# If a particular module is hanging you up, consider +# doing it separately; move it from the current area so that +# the next run will have a better chance or working. +# Also (for the brave only) you might consider hacking +# the s-file for simpler problems: I've successfully changed +# the date of a delta to be in sync, then run "sccs admin -z" +# on the thing. +# +# + After everything finishes, ./SCCS will be moved to ./old-SCCS. +# +# This file may be copied, processed, hacked, mutilated, and +# even destroyed as long as you don't tell anyone you wrote it. +# +# Ken Cox +# Viewlogic Systems, Inc. +# kenstir@viewlogic.com +# ...!harvard!cg-atla!viewlog!kenstir +# +# Various hacks made by Brian Berliner before inclusion in CVS contrib area. +# +# Modified to detect SCCS binary files. If binary, skip the keyword +# substitution and flag the RCS file as binary (using rcs -i -kb). +# -Allan G. Schrum schrum@ofsoptics.com agschrum@mindspring.com +# Fri Sep 26 10:40:40 EDT 2003 +# + + +#we'll assume the user set up the path correctly +# for the Pmax, /usr/ucb/sccs is suid sccs, what a pain +# /usr/local/bin/sccs should override /usr/ucb/sccs there +set path = (/usr/local/bin $path) + + +############################################################ +# Error checking +# +if (! -w .) then + echo "Error: ./ not writeable by you." + exit 1 +endif +if (! -d SCCS) then + echo "Error: ./SCCS directory not found." + exit 1 +endif +set edits = (`sccs tell`) +if ($#edits) then + echo "Error: $#edits file(s) out for edit...clean up before converting." + exit 1 +endif +if (-d RCS) then + echo "Warning: RCS directory exists" + if (`ls -a RCS | wc -l` > 2) then + echo "Error: RCS directory not empty" + exit 1 + endif +else + mkdir RCS +endif + +sccs clean + +set logfile = /tmp/sccs2rcs_$$_log +rm -f $logfile +set tmpfile = /tmp/sccs2rcs_$$_tmp +rm -f $tmpfile +set emptyfile = /tmp/sccs2rcs_$$_empty +echo -n "" > $emptyfile +set initialfile = /tmp/sccs2rcs_$$_init +echo "Initial revision" > $initialfile +set sedfile = /tmp/sccs2rcs_$$_sed +rm -f $sedfile +set revfile = /tmp/sccs2rcs_$$_rev +rm -f $revfile + +# the quotes surround the dollar signs to fool RCS when I check in this script +set sccs_keywords = (\ + '%W%[ ]*%G%'\ + '%W%[ ]*%E%'\ + '%W%'\ + '%Z%%M%[ ]*%I%[ ]*%G%'\ + '%Z%%M%[ ]*%I%[ ]*%E%'\ + '%M%[ ]*%I%[ ]*%G%'\ + '%M%[ ]*%I%[ ]*%E%'\ + '%M%'\ + '%I%'\ + '%G%'\ + '%E%'\ + '%U%') +set rcs_keywords = (\ + '$'Id'$'\ + '$'Id'$'\ + '$'Id'$'\ + '$'SunId'$'\ + '$'SunId'$'\ + '$'Id'$'\ + '$'Id'$'\ + '$'RCSfile'$'\ + '$'Revision'$'\ + '$'Date'$'\ + '$'Date'$'\ + '') + + +############################################################ +# Get some answers from user +# +echo "" +echo "Do you want to be prompted for a description of each" +echo "file as it is checked in to RCS initially?" +echo -n "(y=prompt for description, n=null description) [y] ?" +set ans = $< +if ((_$ans == _) || (_$ans == _y) || (_$ans == _Y)) then + set nodesc = 0 +else + set nodesc = 1 +endif +echo "" +echo "The default keyword substitutions are as follows and are" +echo "applied in the order specified:" +set i = 1 +while ($i <= $#sccs_keywords) +# echo ' '\"$sccs_keywords[$i]\"' ==> '\"$rcs_keywords[$i]\" + echo " $sccs_keywords[$i] ==> $rcs_keywords[$i]" + @ i = $i + 1 +end +echo "" +echo -n "Do you want to change them [n] ?" +set ans = $< +if ((_$ans != _) && (_$ans != _n) && (_$ans != _N)) then + echo "You can't always get what you want." + echo "Edit this script file and change the variables:" + echo ' $sccs_keywords' + echo ' $rcs_keywords' +else + echo "good idea." +endif + +# create the sed script +set i = 1 +while ($i <= $#sccs_keywords) + echo "s,$sccs_keywords[$i],$rcs_keywords[$i],g" >> $sedfile + @ i = $i + 1 +end + +onintr ERROR + +sort -k 1,1 /dev/null >& /dev/null +if ($status == 0) then + set sort_each_field = '-k 1 -k 2 -k 3 -k 4 -k 5 -k 6 -k 7 -k 8 -k 9' +else + set sort_each_field = '+0 +1 +2 +3 +4 +5 +6 +7 +8' +endif + +############################################################ +# Loop over every s-file in SCCS dir +# +foreach sfile (SCCS/s.*) + # get rid of the "s." at the beginning of the name + set file = `echo $sfile:t | sed -e "s/^..//"` + + # work on each rev of that file in ascending order + set firsttime = 1 + + # Only scan the file up to the "I" keyword, then see if + # the "f" keyword is set to binary. The SCCS file has + # -aI denoting the start of the file (or end of header). + set binary = (`sed -e '/^.I/,$d' < $sfile | grep '^.f e 1$'`) + #if ($#binary) then + # echo This is a binary file + #else + # echo This is not a binary file + #endif + + sccs prs $file | grep "^D " | @AWK@ '{print $2}' | sed -e 's/\./ /g' | sort -n -u $sort_each_field | sed -e 's/ /./g' > $revfile + foreach rev (`cat $revfile`) + if ($status != 0) goto ERROR + + # get file into current dir and get stats + + # Is the substr stuff and the +0 in the following awk script really + # necessary? It seems to me that if we didn't find the date format + # we expected in the output we have other problems. + # Note: Solaris awk does not like the following line. Use gawk + # mawk, or nawk instead. + set date = `sccs prs -r$rev $file | @AWK@ '/^D / {print (substr($3,0,2)+0<70?20:19) $3, $4; exit}'` + set author = `sccs prs -r$rev $file | @AWK@ '/^D / {print $5; exit}'` + echo "" + echo "==> file $file, rev=$rev, date=$date, author=$author" + sccs edit -r$rev $file >>& $logfile + if ($status != 0) goto ERROR + echo checked out of SCCS + + # add RCS keywords in place of SCCS keywords (only if not binary) + if ($#binary == 0) then + sed -f $sedfile $file > $tmpfile + if ($status != 0) goto ERROR + echo performed keyword substitutions + cp $tmpfile $file + endif + + # check file into RCS + if ($firsttime) then + set firsttime = 0 + + if ($#binary) then + echo this is a binary file + # Mark initial, empty file as binary + rcs -i -kb -t$emptyfile $file + endif + + if ($nodesc) then + echo about to do ci + echo ci -f -r$rev -d"$date" -w$author -t$emptyfile $file + ci -f -r$rev -d"$date" -w$author -t$emptyfile $file < $initialfile >>& $logfile + if ($status != 0) goto ERROR + echo initial rev checked into RCS without description + else + echo "" + echo Enter a brief description of the file $file \(end w/ Ctrl-D\): + cat > $tmpfile + ci -f -r$rev -d"$date" -w$author -t$tmpfile $file < $initialfile >>& $logfile + if ($status != 0) goto ERROR + echo initial rev checked into RCS + endif + else + # get RCS lock + set lckrev = `echo $rev | sed -e 's/\.[0-9]*$//'` + if ("$lckrev" =~ [0-9]*.*) then + # need to lock the brach -- it is OK if the lock fails + rcs -l$lckrev $file >>& $logfile + else + # need to lock the trunk -- must succeed + rcs -l $file >>& $logfile + if ($status != 0) goto ERROR + endif + echo got lock + sccs prs -r$rev $file | grep "." > $tmpfile + # it's OK if grep fails here and gives status == 1 + # put the delta message in $tmpfile + ed $tmpfile >>& $logfile <>& $logfile + if ($status != 0) goto ERROR + echo checked into RCS + endif + sccs unedit $file >>& $logfile + if ($status != 0) goto ERROR + end + rm -f $file +end + + +############################################################ +# Clean up +# +echo cleaning up... +mv SCCS old-SCCS +rm -f $tmpfile $emptyfile $initialfile $sedfile +echo =================================================== +echo " Conversion Completed Successfully" +echo "" +echo " SCCS history now in old-SCCS/" +echo =================================================== +set exitval = 0 +goto cleanup + +ERROR: +foreach f (`sccs tell`) + sccs unedit $f +end +echo "" +echo "" +echo Danger\! Danger\! +echo Some command exited with a non-zero exit status. +echo Log file exists in $logfile. +echo "" +echo Incomplete history in ./RCS -- remove it +echo Original unchanged history in ./SCCS +set exitval = 1 + +cleanup: +# leave log file +rm -f $tmpfile $emptyfile $initialfile $sedfile $revfile + +exit $exitval diff --git a/contrib/cvs-1.12.9/diff/README b/contrib/cvs-1.12.9/diff/README new file mode 100644 index 0000000000..0af33d733d --- /dev/null +++ b/contrib/cvs-1.12.9/diff/README @@ -0,0 +1,8 @@ +The files in this directory come from the GNU diffutils project +/, +and, if they don't, they should. + +What this means is that bug fixes and enhancements to this code should be sent +to the diffutils project and then reimported here after the diffutils +developers approve and adopt the change. Changes should not be made locally +without good reason! diff --git a/contrib/cvs-1.12.9/diff/analyze.c b/contrib/cvs-1.12.9/diff/analyze.c new file mode 100644 index 0000000000..3262444e6b --- /dev/null +++ b/contrib/cvs-1.12.9/diff/analyze.c @@ -0,0 +1,1082 @@ +/* Analyze file differences for GNU DIFF. + Copyright (C) 1988, 1989, 1992, 1993, 1997 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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 basic algorithm is described in: + "An O(ND) Difference Algorithm and its Variations", Eugene Myers, + Algorithmica Vol. 1 No. 2, 1986, pp. 251-266; + see especially section 4.2, which describes the variation used below. + Unless the --minimal option is specified, this code uses the TOO_EXPENSIVE + heuristic, by Paul Eggert, to limit the cost to O(N**1.5 log N) + at the price of producing suboptimal output for large inputs with + many differences. + + The basic algorithm was independently discovered as described in: + "Algorithms for Approximate String Matching", E. Ukkonen, + Information and Control Vol. 64, 1985, pp. 100-118. */ + +#include "diff.h" +#include "cmpbuf.h" + +extern int no_discards; + +static int *xvec, *yvec; /* Vectors being compared. */ +static int *fdiag; /* Vector, indexed by diagonal, containing + 1 + the X coordinate of the point furthest + along the given diagonal in the forward + search of the edit matrix. */ +static int *bdiag; /* Vector, indexed by diagonal, containing + the X coordinate of the point furthest + along the given diagonal in the backward + search of the edit matrix. */ +static int too_expensive; /* Edit scripts longer than this are too + expensive to compute. */ + +#define SNAKE_LIMIT 20 /* Snakes bigger than this are considered `big'. */ + +struct partition +{ + int xmid, ymid; /* Midpoints of this partition. */ + int lo_minimal; /* Nonzero if low half will be analyzed minimally. */ + int hi_minimal; /* Likewise for high half. */ +}; + +static int diag PARAMS((int, int, int, int, int, struct partition *)); +static struct change *add_change PARAMS((int, int, int, int, struct change *)); +static struct change *build_reverse_script PARAMS((struct file_data const[])); +static struct change *build_script PARAMS((struct file_data const[])); +static void briefly_report PARAMS((int, struct file_data const[])); +static void compareseq PARAMS((int, int, int, int, int)); +static void discard_confusing_lines PARAMS((struct file_data[])); +static void shift_boundaries PARAMS((struct file_data[])); + +/* Find the midpoint of the shortest edit script for a specified + portion of the two files. + + Scan from the beginnings of the files, and simultaneously from the ends, + doing a breadth-first search through the space of edit-sequence. + When the two searches meet, we have found the midpoint of the shortest + edit sequence. + + If MINIMAL is nonzero, find the minimal edit script regardless + of expense. Otherwise, if the search is too expensive, use + heuristics to stop the search and report a suboptimal answer. + + Set PART->(XMID,YMID) to the midpoint (XMID,YMID). The diagonal number + XMID - YMID equals the number of inserted lines minus the number + of deleted lines (counting only lines before the midpoint). + Return the approximate edit cost; this is the total number of + lines inserted or deleted (counting only lines before the midpoint), + unless a heuristic is used to terminate the search prematurely. + + Set PART->LEFT_MINIMAL to nonzero iff the minimal edit script for the + left half of the partition is known; similarly for PART->RIGHT_MINIMAL. + + This function assumes that the first lines of the specified portions + of the two files do not match, and likewise that the last lines do not + match. The caller must trim matching lines from the beginning and end + of the portions it is going to specify. + + If we return the "wrong" partitions, + the worst this can do is cause suboptimal diff output. + It cannot cause incorrect diff output. */ + +static int +diag (xoff, xlim, yoff, ylim, minimal, part) + int xoff, xlim, yoff, ylim, minimal; + struct partition *part; +{ + int *const fd = fdiag; /* Give the compiler a chance. */ + int *const bd = bdiag; /* Additional help for the compiler. */ + int const *const xv = xvec; /* Still more help for the compiler. */ + int const *const yv = yvec; /* And more and more . . . */ + int const dmin = xoff - ylim; /* Minimum valid diagonal. */ + int const dmax = xlim - yoff; /* Maximum valid diagonal. */ + int const fmid = xoff - yoff; /* Center diagonal of top-down search. */ + int const bmid = xlim - ylim; /* Center diagonal of bottom-up search. */ + int fmin = fmid, fmax = fmid; /* Limits of top-down search. */ + int bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */ + int c; /* Cost. */ + int odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd + diagonal with respect to the northwest. */ + + fd[fmid] = xoff; + bd[bmid] = xlim; + + for (c = 1;; ++c) + { + int d; /* Active diagonal. */ + int big_snake = 0; + + /* Extend the top-down search by an edit step in each diagonal. */ + fmin > dmin ? fd[--fmin - 1] = -1 : ++fmin; + fmax < dmax ? fd[++fmax + 1] = -1 : --fmax; + for (d = fmax; d >= fmin; d -= 2) + { + int x, y, oldx, tlo = fd[d - 1], thi = fd[d + 1]; + + if (tlo >= thi) + x = tlo + 1; + else + x = thi; + oldx = x; + y = x - d; + while (x < xlim && y < ylim && xv[x] == yv[y]) + ++x, ++y; + if (x - oldx > SNAKE_LIMIT) + big_snake = 1; + fd[d] = x; + if (odd && bmin <= d && d <= bmax && bd[d] <= x) + { + part->xmid = x; + part->ymid = y; + part->lo_minimal = part->hi_minimal = 1; + return 2 * c - 1; + } + } + + /* Similarly extend the bottom-up search. */ + bmin > dmin ? bd[--bmin - 1] = INT_MAX : ++bmin; + bmax < dmax ? bd[++bmax + 1] = INT_MAX : --bmax; + for (d = bmax; d >= bmin; d -= 2) + { + int x, y, oldx, tlo = bd[d - 1], thi = bd[d + 1]; + + if (tlo < thi) + x = tlo; + else + x = thi - 1; + oldx = x; + y = x - d; + while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1]) + --x, --y; + if (oldx - x > SNAKE_LIMIT) + big_snake = 1; + bd[d] = x; + if (!odd && fmin <= d && d <= fmax && x <= fd[d]) + { + part->xmid = x; + part->ymid = y; + part->lo_minimal = part->hi_minimal = 1; + return 2 * c; + } + } + + if (minimal) + continue; + + /* Heuristic: check occasionally for a diagonal that has made + lots of progress compared with the edit distance. + If we have any such, find the one that has made the most + progress and return it as if it had succeeded. + + With this heuristic, for files with a constant small density + of changes, the algorithm is linear in the file size. */ + + if (c > 200 && big_snake && heuristic) + { + int best; + + best = 0; + for (d = fmax; d >= fmin; d -= 2) + { + int dd = d - fmid; + int x = fd[d]; + int y = x - d; + int v = (x - xoff) * 2 - dd; + if (v > 12 * (c + (dd < 0 ? -dd : dd))) + { + if (v > best + && xoff + SNAKE_LIMIT <= x && x < xlim + && yoff + SNAKE_LIMIT <= y && y < ylim) + { + /* We have a good enough best diagonal; + now insist that it end with a significant snake. */ + int k; + + for (k = 1; xv[x - k] == yv[y - k]; k++) + if (k == SNAKE_LIMIT) + { + best = v; + part->xmid = x; + part->ymid = y; + break; + } + } + } + } + if (best > 0) + { + part->lo_minimal = 1; + part->hi_minimal = 0; + return 2 * c - 1; + } + + best = 0; + for (d = bmax; d >= bmin; d -= 2) + { + int dd = d - bmid; + int x = bd[d]; + int y = x - d; + int v = (xlim - x) * 2 + dd; + if (v > 12 * (c + (dd < 0 ? -dd : dd))) + { + if (v > best + && xoff < x && x <= xlim - SNAKE_LIMIT + && yoff < y && y <= ylim - SNAKE_LIMIT) + { + /* We have a good enough best diagonal; + now insist that it end with a significant snake. */ + int k; + + for (k = 0; xv[x + k] == yv[y + k]; k++) + if (k == SNAKE_LIMIT - 1) + { + best = v; + part->xmid = x; + part->ymid = y; + break; + } + } + } + } + if (best > 0) + { + part->lo_minimal = 0; + part->hi_minimal = 1; + return 2 * c - 1; + } + } + + /* Heuristic: if we've gone well beyond the call of duty, + give up and report halfway between our best results so far. */ + if (c >= too_expensive) + { + int fxybest, fxbest; + int bxybest, bxbest; + + fxbest = bxbest = 0; /* Pacify `gcc -Wall'. */ + + /* Find forward diagonal that maximizes X + Y. */ + fxybest = -1; + for (d = fmax; d >= fmin; d -= 2) + { + int x = min (fd[d], xlim); + int y = x - d; + if (ylim < y) + x = ylim + d, y = ylim; + if (fxybest < x + y) + { + fxybest = x + y; + fxbest = x; + } + } + + /* Find backward diagonal that minimizes X + Y. */ + bxybest = INT_MAX; + for (d = bmax; d >= bmin; d -= 2) + { + int x = max (xoff, bd[d]); + int y = x - d; + if (y < yoff) + x = yoff + d, y = yoff; + if (x + y < bxybest) + { + bxybest = x + y; + bxbest = x; + } + } + + /* Use the better of the two diagonals. */ + if ((xlim + ylim) - bxybest < fxybest - (xoff + yoff)) + { + part->xmid = fxbest; + part->ymid = fxybest - fxbest; + part->lo_minimal = 1; + part->hi_minimal = 0; + } + else + { + part->xmid = bxbest; + part->ymid = bxybest - bxbest; + part->lo_minimal = 0; + part->hi_minimal = 1; + } + return 2 * c - 1; + } + } +} + +/* Compare in detail contiguous subsequences of the two files + which are known, as a whole, to match each other. + + The results are recorded in the vectors files[N].changed_flag, by + storing a 1 in the element for each line that is an insertion or deletion. + + The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. + + Note that XLIM, YLIM are exclusive bounds. + All line numbers are origin-0 and discarded lines are not counted. + + If MINIMAL is nonzero, find a minimal difference no matter how + expensive it is. */ + +static void +compareseq (xoff, xlim, yoff, ylim, minimal) + int xoff, xlim, yoff, ylim, minimal; +{ + int * const xv = xvec; /* Help the compiler. */ + int * const yv = yvec; + + /* Slide down the bottom initial diagonal. */ + while (xoff < xlim && yoff < ylim && xv[xoff] == yv[yoff]) + ++xoff, ++yoff; + /* Slide up the top initial diagonal. */ + while (xlim > xoff && ylim > yoff && xv[xlim - 1] == yv[ylim - 1]) + --xlim, --ylim; + + /* Handle simple cases. */ + if (xoff == xlim) + while (yoff < ylim) + files[1].changed_flag[files[1].realindexes[yoff++]] = 1; + else if (yoff == ylim) + while (xoff < xlim) + files[0].changed_flag[files[0].realindexes[xoff++]] = 1; + else + { + int c; + struct partition part; + + /* Find a point of correspondence in the middle of the files. */ + + c = diag (xoff, xlim, yoff, ylim, minimal, &part); + + if (c == 1) + { + /* This should be impossible, because it implies that + one of the two subsequences is empty, + and that case was handled above without calling `diag'. + Let's verify that this is true. */ + abort (); +#if 0 + /* The two subsequences differ by a single insert or delete; + record it and we are done. */ + if (part.xmid - part.ymid < xoff - yoff) + files[1].changed_flag[files[1].realindexes[part.ymid - 1]] = 1; + else + files[0].changed_flag[files[0].realindexes[part.xmid]] = 1; +#endif + } + else + { + /* Use the partitions to split this problem into subproblems. */ + compareseq (xoff, part.xmid, yoff, part.ymid, part.lo_minimal); + compareseq (part.xmid, xlim, part.ymid, ylim, part.hi_minimal); + } + } +} + +/* Discard lines from one file that have no matches in the other file. + + A line which is discarded will not be considered by the actual + comparison algorithm; it will be as if that line were not in the file. + The file's `realindexes' table maps virtual line numbers + (which don't count the discarded lines) into real line numbers; + this is how the actual comparison algorithm produces results + that are comprehensible when the discarded lines are counted. + + When we discard a line, we also mark it as a deletion or insertion + so that it will be printed in the output. */ + +static void +discard_confusing_lines (filevec) + struct file_data filevec[]; +{ + unsigned int f, i; + char *discarded[2]; + int *equiv_count[2]; + int *p; + + /* Allocate our results. */ + p = (int *) xmalloc ((filevec[0].buffered_lines + filevec[1].buffered_lines) + * (2 * sizeof (int))); + for (f = 0; f < 2; f++) + { + filevec[f].undiscarded = p; p += filevec[f].buffered_lines; + filevec[f].realindexes = p; p += filevec[f].buffered_lines; + } + + /* Set up equiv_count[F][I] as the number of lines in file F + that fall in equivalence class I. */ + + p = (int *) xmalloc (filevec[0].equiv_max * (2 * sizeof (int))); + equiv_count[0] = p; + equiv_count[1] = p + filevec[0].equiv_max; + bzero (p, filevec[0].equiv_max * (2 * sizeof (int))); + + for (i = 0; i < filevec[0].buffered_lines; ++i) + ++equiv_count[0][filevec[0].equivs[i]]; + for (i = 0; i < filevec[1].buffered_lines; ++i) + ++equiv_count[1][filevec[1].equivs[i]]; + + /* Set up tables of which lines are going to be discarded. */ + + discarded[0] = xmalloc (sizeof (char) + * (filevec[0].buffered_lines + + filevec[1].buffered_lines)); + discarded[1] = discarded[0] + filevec[0].buffered_lines; + bzero (discarded[0], sizeof (char) * (filevec[0].buffered_lines + + filevec[1].buffered_lines)); + + /* Mark to be discarded each line that matches no line of the other file. + If a line matches many lines, mark it as provisionally discardable. */ + + for (f = 0; f < 2; f++) + { + unsigned int end = filevec[f].buffered_lines; + char *discards = discarded[f]; + int *counts = equiv_count[1 - f]; + int *equivs = filevec[f].equivs; + unsigned int many = 5; + unsigned int tem = end / 64; + + /* Multiply MANY by approximate square root of number of lines. + That is the threshold for provisionally discardable lines. */ + while ((tem = tem >> 2) > 0) + many *= 2; + + for (i = 0; i < end; i++) + { + int nmatch; + if (equivs[i] == 0) + continue; + nmatch = counts[equivs[i]]; + if (nmatch == 0) + discards[i] = 1; + else if (nmatch > many) + discards[i] = 2; + } + } + + /* Don't really discard the provisional lines except when they occur + in a run of discardables, with nonprovisionals at the beginning + and end. */ + + for (f = 0; f < 2; f++) + { + unsigned int end = filevec[f].buffered_lines; + register char *discards = discarded[f]; + + for (i = 0; i < end; i++) + { + /* Cancel provisional discards not in middle of run of discards. */ + if (discards[i] == 2) + discards[i] = 0; + else if (discards[i] != 0) + { + /* We have found a nonprovisional discard. */ + register int j; + unsigned int length; + unsigned int provisional = 0; + + /* Find end of this run of discardable lines. + Count how many are provisionally discardable. */ + for (j = i; j < end; j++) + { + if (discards[j] == 0) + break; + if (discards[j] == 2) + ++provisional; + } + + /* Cancel provisional discards at end, and shrink the run. */ + while (j > i && discards[j - 1] == 2) + discards[--j] = 0, --provisional; + + /* Now we have the length of a run of discardable lines + whose first and last are not provisional. */ + length = j - i; + + /* If 1/4 of the lines in the run are provisional, + cancel discarding of all provisional lines in the run. */ + if (provisional * 4 > length) + { + while (j > i) + if (discards[--j] == 2) + discards[j] = 0; + } + else + { + register unsigned int consec; + unsigned int minimum = 1; + unsigned int tem = length / 4; + + /* MINIMUM is approximate square root of LENGTH/4. + A subrun of two or more provisionals can stand + when LENGTH is at least 16. + A subrun of 4 or more can stand when LENGTH >= 64. */ + while ((tem = tem >> 2) > 0) + minimum *= 2; + minimum++; + + /* Cancel any subrun of MINIMUM or more provisionals + within the larger run. */ + for (j = 0, consec = 0; j < length; j++) + if (discards[i + j] != 2) + consec = 0; + else if (minimum == ++consec) + /* Back up to start of subrun, to cancel it all. */ + j -= consec; + else if (minimum < consec) + discards[i + j] = 0; + + /* Scan from beginning of run + until we find 3 or more nonprovisionals in a row + or until the first nonprovisional at least 8 lines in. + Until that point, cancel any provisionals. */ + for (j = 0, consec = 0; j < length; j++) + { + if (j >= 8 && discards[i + j] == 1) + break; + if (discards[i + j] == 2) + consec = 0, discards[i + j] = 0; + else if (discards[i + j] == 0) + consec = 0; + else + consec++; + if (consec == 3) + break; + } + + /* I advances to the last line of the run. */ + i += length - 1; + + /* Same thing, from end. */ + for (j = 0, consec = 0; j < length; j++) + { + if (j >= 8 && discards[i - j] == 1) + break; + if (discards[i - j] == 2) + consec = 0, discards[i - j] = 0; + else if (discards[i - j] == 0) + consec = 0; + else + consec++; + if (consec == 3) + break; + } + } + } + } + } + + /* Actually discard the lines. */ + for (f = 0; f < 2; f++) + { + char *discards = discarded[f]; + unsigned int end = filevec[f].buffered_lines; + unsigned int j = 0; + for (i = 0; i < end; ++i) + if (no_discards || discards[i] == 0) + { + filevec[f].undiscarded[j] = filevec[f].equivs[i]; + filevec[f].realindexes[j++] = i; + } + else + filevec[f].changed_flag[i] = 1; + filevec[f].nondiscarded_lines = j; + } + + free (discarded[0]); + free (equiv_count[0]); +} + +/* Adjust inserts/deletes of identical lines to join changes + as much as possible. + + We do something when a run of changed lines include a + line at one end and have an excluded, identical line at the other. + We are free to choose which identical line is included. + `compareseq' usually chooses the one at the beginning, + but usually it is cleaner to consider the following identical line + to be the "change". */ + +int inhibit; + +static void +shift_boundaries (filevec) + struct file_data filevec[]; +{ + int f; + + if (inhibit) + return; + + for (f = 0; f < 2; f++) + { + char *changed = filevec[f].changed_flag; + char const *other_changed = filevec[1-f].changed_flag; + int const *equivs = filevec[f].equivs; + int i = 0; + int j = 0; + int i_end = filevec[f].buffered_lines; + + while (1) + { + int runlength, start, corresponding; + + /* Scan forwards to find beginning of another run of changes. + Also keep track of the corresponding point in the other file. */ + + while (i < i_end && changed[i] == 0) + { + while (other_changed[j++]) + continue; + i++; + } + + if (i == i_end) + break; + + start = i; + + /* Find the end of this run of changes. */ + + while (changed[++i]) + continue; + while (other_changed[j]) + j++; + + do + { + /* Record the length of this run of changes, so that + we can later determine whether the run has grown. */ + runlength = i - start; + + /* Move the changed region back, so long as the + previous unchanged line matches the last changed one. + This merges with previous changed regions. */ + + while (start && equivs[start - 1] == equivs[i - 1]) + { + changed[--start] = 1; + changed[--i] = 0; + while (changed[start - 1]) + start--; + while (other_changed[--j]) + continue; + } + + /* Set CORRESPONDING to the end of the changed run, at the last + point where it corresponds to a changed run in the other file. + CORRESPONDING == I_END means no such point has been found. */ + corresponding = other_changed[j - 1] ? i : i_end; + + /* Move the changed region forward, so long as the + first changed line matches the following unchanged one. + This merges with following changed regions. + Do this second, so that if there are no merges, + the changed region is moved forward as far as possible. */ + + while (i != i_end && equivs[start] == equivs[i]) + { + changed[start++] = 0; + changed[i++] = 1; + while (changed[i]) + i++; + while (other_changed[++j]) + corresponding = i; + } + } + while (runlength != i - start); + + /* If possible, move the fully-merged run of changes + back to a corresponding run in the other file. */ + + while (corresponding < i) + { + changed[--start] = 1; + changed[--i] = 0; + while (other_changed[--j]) + continue; + } + } + } +} + +/* Cons an additional entry onto the front of an edit script OLD. + LINE0 and LINE1 are the first affected lines in the two files (origin 0). + DELETED is the number of lines deleted here from file 0. + INSERTED is the number of lines inserted here in file 1. + + If DELETED is 0 then LINE0 is the number of the line before + which the insertion was done; vice versa for INSERTED and LINE1. */ + +static struct change * +add_change (line0, line1, deleted, inserted, old) + int line0, line1, deleted, inserted; + struct change *old; +{ + struct change *new = (struct change *) xmalloc (sizeof (struct change)); + + new->line0 = line0; + new->line1 = line1; + new->inserted = inserted; + new->deleted = deleted; + new->link = old; + return new; +} + +/* Scan the tables of which lines are inserted and deleted, + producing an edit script in reverse order. */ + +static struct change * +build_reverse_script (filevec) + struct file_data const filevec[]; +{ + struct change *script = 0; + char *changed0 = filevec[0].changed_flag; + char *changed1 = filevec[1].changed_flag; + int len0 = filevec[0].buffered_lines; + int len1 = filevec[1].buffered_lines; + + /* Note that changedN[len0] does exist, and contains 0. */ + + int i0 = 0, i1 = 0; + + while (i0 < len0 || i1 < len1) + { + if (changed0[i0] || changed1[i1]) + { + int line0 = i0, line1 = i1; + + /* Find # lines changed here in each file. */ + while (changed0[i0]) ++i0; + while (changed1[i1]) ++i1; + + /* Record this change. */ + script = add_change (line0, line1, i0 - line0, i1 - line1, script); + } + + /* We have reached lines in the two files that match each other. */ + i0++, i1++; + } + + return script; +} + +/* Scan the tables of which lines are inserted and deleted, + producing an edit script in forward order. */ + +static struct change * +build_script (filevec) + struct file_data const filevec[]; +{ + struct change *script = 0; + char *changed0 = filevec[0].changed_flag; + char *changed1 = filevec[1].changed_flag; + int i0 = filevec[0].buffered_lines, i1 = filevec[1].buffered_lines; + + /* Note that changedN[-1] does exist, and contains 0. */ + + while (i0 >= 0 || i1 >= 0) + { + if (changed0[i0 - 1] || changed1[i1 - 1]) + { + int line0 = i0, line1 = i1; + + /* Find # lines changed here in each file. */ + while (changed0[i0 - 1]) --i0; + while (changed1[i1 - 1]) --i1; + + /* Record this change. */ + script = add_change (i0, i1, line0 - i0, line1 - i1, script); + } + + /* We have reached lines in the two files that match each other. */ + i0--, i1--; + } + + return script; +} + +/* If CHANGES, briefly report that two files differed. */ +static void +briefly_report (changes, filevec) + int changes; + struct file_data const filevec[]; +{ + if (changes) + message (no_details_flag ? "Files %s and %s differ\n" + : "Binary files %s and %s differ\n", + filevec[0].name, filevec[1].name); +} + +/* Report the differences of two files. DEPTH is the current directory + depth. */ +int +diff_2_files (filevec, depth) + struct file_data filevec[]; + int depth; +{ + int diags; + int i; + struct change *e, *p; + struct change *script; + int changes; + + + /* If we have detected that either file is binary, + compare the two files as binary. This can happen + only when the first chunk is read. + Also, --brief without any --ignore-* options means + we can speed things up by treating the files as binary. */ + + if (read_files (filevec, no_details_flag & ~ignore_some_changes)) + { + /* Files with different lengths must be different. */ + if (filevec[0].stat.st_size != filevec[1].stat.st_size + && (filevec[0].desc < 0 || S_ISREG (filevec[0].stat.st_mode)) + && (filevec[1].desc < 0 || S_ISREG (filevec[1].stat.st_mode))) + changes = 1; + + /* Standard input equals itself. */ + else if (filevec[0].desc == filevec[1].desc) + changes = 0; + + else + /* Scan both files, a buffer at a time, looking for a difference. */ + { + /* Allocate same-sized buffers for both files. */ + size_t buffer_size = buffer_lcm (STAT_BLOCKSIZE (filevec[0].stat), + STAT_BLOCKSIZE (filevec[1].stat)); + for (i = 0; i < 2; i++) + filevec[i].buffer = xrealloc (filevec[i].buffer, buffer_size); + + for (;; filevec[0].buffered_chars = filevec[1].buffered_chars = 0) + { + /* Read a buffer's worth from both files. */ + for (i = 0; i < 2; i++) + if (0 <= filevec[i].desc) + while (filevec[i].buffered_chars != buffer_size) + { + int r = read (filevec[i].desc, + filevec[i].buffer + + filevec[i].buffered_chars, + buffer_size - filevec[i].buffered_chars); + if (r == 0) + break; + if (r < 0) + pfatal_with_name (filevec[i].name); + filevec[i].buffered_chars += r; + } + + /* If the buffers differ, the files differ. */ + if (filevec[0].buffered_chars != filevec[1].buffered_chars + || (filevec[0].buffered_chars != 0 + && memcmp (filevec[0].buffer, + filevec[1].buffer, + filevec[0].buffered_chars) != 0)) + { + changes = 1; + break; + } + + /* If we reach end of file, the files are the same. */ + if (filevec[0].buffered_chars != buffer_size) + { + changes = 0; + break; + } + } + } + + briefly_report (changes, filevec); + } + else + { + /* Allocate vectors for the results of comparison: + a flag for each line of each file, saying whether that line + is an insertion or deletion. + Allocate an extra element, always zero, at each end of each vector. */ + + size_t s = filevec[0].buffered_lines + filevec[1].buffered_lines + 4; + filevec[0].changed_flag = xmalloc (s); + bzero (filevec[0].changed_flag, s); + filevec[0].changed_flag++; + filevec[1].changed_flag = filevec[0].changed_flag + + filevec[0].buffered_lines + 2; + + /* Some lines are obviously insertions or deletions + because they don't match anything. Detect them now, and + avoid even thinking about them in the main comparison algorithm. */ + + discard_confusing_lines (filevec); + + /* Now do the main comparison algorithm, considering just the + undiscarded lines. */ + + xvec = filevec[0].undiscarded; + yvec = filevec[1].undiscarded; + diags = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3; + fdiag = (int *) xmalloc (diags * (2 * sizeof (int))); + bdiag = fdiag + diags; + fdiag += filevec[1].nondiscarded_lines + 1; + bdiag += filevec[1].nondiscarded_lines + 1; + + /* Set TOO_EXPENSIVE to be approximate square root of input size, + bounded below by 256. */ + too_expensive = 1; + for (i = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines; + i != 0; i >>= 2) + too_expensive <<= 1; + too_expensive = max (256, too_expensive); + + files[0] = filevec[0]; + files[1] = filevec[1]; + + compareseq (0, filevec[0].nondiscarded_lines, + 0, filevec[1].nondiscarded_lines, no_discards); + + free (fdiag - (filevec[1].nondiscarded_lines + 1)); + + /* Modify the results slightly to make them prettier + in cases where that can validly be done. */ + + shift_boundaries (filevec); + + /* Get the results of comparison in the form of a chain + of `struct change's -- an edit script. */ + + if (output_style == OUTPUT_ED) + script = build_reverse_script (filevec); + else + script = build_script (filevec); + + /* Set CHANGES if we had any diffs. + If some changes are ignored, we must scan the script to decide. */ + if (ignore_blank_lines_flag || ignore_regexp_list) + { + struct change *next = script; + changes = 0; + + while (next && changes == 0) + { + struct change *this, *end; + int first0, last0, first1, last1, deletes, inserts; + + /* Find a set of changes that belong together. */ + this = next; + end = find_change (next); + + /* Disconnect them from the rest of the changes, making them + a hunk, and remember the rest for next iteration. */ + next = end->link; + end->link = 0; + + /* Determine whether this hunk is really a difference. */ + analyze_hunk (this, &first0, &last0, &first1, &last1, + &deletes, &inserts); + + /* Reconnect the script so it will all be freed properly. */ + end->link = next; + + if (deletes || inserts) + changes = 1; + } + } + else + changes = (script != 0); + + if (no_details_flag) + briefly_report (changes, filevec); + else + { + if (changes || ! no_diff_means_no_output) + { + /* Record info for starting up output, + to be used if and when we have some output to print. */ + setup_output (files[0].name, files[1].name, depth); + + switch (output_style) + { + case OUTPUT_CONTEXT: + print_context_script (script, 0); + break; + + case OUTPUT_UNIFIED: + print_context_script (script, 1); + break; + + case OUTPUT_ED: + print_ed_script (script); + break; + + case OUTPUT_FORWARD_ED: + pr_forward_ed_script (script); + break; + + case OUTPUT_RCS: + print_rcs_script (script); + break; + + case OUTPUT_NORMAL: + print_normal_script (script); + break; + + case OUTPUT_IFDEF: + print_ifdef_script (script); + break; + + case OUTPUT_SDIFF: + print_sdiff_script (script); + } + + finish_output (); + } + } + + free (filevec[0].undiscarded); + + free (filevec[0].changed_flag - 1); + + for (i = 1; i >= 0; --i) + free (filevec[i].equivs); + + for (i = 0; i < 2; ++i) + free (filevec[i].linbuf + filevec[i].linbuf_base); + + for (e = script; e; e = p) + { + p = e->link; + free (e); + } + + if (! ROBUST_OUTPUT_STYLE (output_style)) + for (i = 0; i < 2; ++i) + if (filevec[i].missing_newline) + { + diff_error ("No newline at end of file %s", filevec[i].name, ""); + changes = 2; + } + } + + if (filevec[0].buffer != filevec[1].buffer) + free (filevec[0].buffer); + free (filevec[1].buffer); + + return changes; +} diff --git a/contrib/cvs-1.12.9/diff/cmpbuf.c b/contrib/cvs-1.12.9/diff/cmpbuf.c new file mode 100644 index 0000000000..2820dfa5a2 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/cmpbuf.c @@ -0,0 +1,38 @@ +/* Buffer primitives for comparison operations. + Copyright (C) 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + */ + +#include "system.h" +#include "cmpbuf.h" + +/* Least common multiple of two buffer sizes A and B. */ + +size_t +buffer_lcm (a, b) + size_t a, b; +{ + size_t m, n, r; + + /* Yield reasonable values if buffer sizes are zero. */ + if (!a) + return b ? b : 8 * 1024; + if (!b) + return a; + + /* n = gcd (a, b) */ + for (m = a, n = b; (r = m % n) != 0; m = n, n = r) + continue; + + return a/n * b; +} diff --git a/contrib/cvs-1.12.9/diff/cmpbuf.h b/contrib/cvs-1.12.9/diff/cmpbuf.h new file mode 100644 index 0000000000..b7b965d857 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/cmpbuf.h @@ -0,0 +1,18 @@ +/* Buffer primitives for comparison operations. + Copyright (C) 1993 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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. + +*/ + +size_t buffer_lcm PARAMS((size_t, size_t)); diff --git a/contrib/cvs-1.12.9/diff/context.c b/contrib/cvs-1.12.9/diff/context.c new file mode 100644 index 0000000000..c4562c94b1 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/context.c @@ -0,0 +1,462 @@ +/* Context-format output routines for GNU DIFF. + Copyright (C) 1988,1989,1991,1992,1993,1994,1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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 "diff.h" + +static struct change *find_hunk PARAMS((struct change *)); +static void find_function PARAMS((struct file_data const *, int, char const **, size_t *)); +static void mark_ignorable PARAMS((struct change *)); +static void pr_context_hunk PARAMS((struct change *)); +static void pr_unidiff_hunk PARAMS((struct change *)); +static void print_context_label PARAMS ((char const *, struct file_data *, char const *)); +static void print_context_number_range PARAMS((struct file_data const *, int, int)); +static void print_unidiff_number_range PARAMS((struct file_data const *, int, int)); + +/* Last place find_function started searching from. */ +static int find_function_last_search; + +/* The value find_function returned when it started searching there. */ +static int find_function_last_match; + +/* Print a label for a context diff, with a file name and date or a label. */ + +static void +print_context_label (mark, inf, label) + char const *mark; + struct file_data *inf; + char const *label; +{ + if (label) + printf_output ("%s %s\n", mark, label); + else + { + char const *ct = ctime (&inf->stat.st_mtime); + if (!ct) + ct = "?\n"; + /* See Posix.2 section 4.17.6.1.4 for this format. */ + printf_output ("%s %s\t%s", mark, inf->name, ct); + } +} + +/* Print a header for a context diff, with the file names and dates. */ + +void +print_context_header (inf, unidiff_flag) + struct file_data inf[]; + int unidiff_flag; +{ + if (unidiff_flag) + { + print_context_label ("---", &inf[0], file_label[0]); + print_context_label ("+++", &inf[1], file_label[1]); + } + else + { + print_context_label ("***", &inf[0], file_label[0]); + print_context_label ("---", &inf[1], file_label[1]); + } +} + +/* Print an edit script in context format. */ + +void +print_context_script (script, unidiff_flag) + struct change *script; + int unidiff_flag; +{ + if (ignore_blank_lines_flag || ignore_regexp_list) + mark_ignorable (script); + else + { + struct change *e; + for (e = script; e; e = e->link) + e->ignore = 0; + } + + find_function_last_search = - files[0].prefix_lines; + find_function_last_match = find_function_last_search - 1; + + if (unidiff_flag) + print_script (script, find_hunk, pr_unidiff_hunk); + else + print_script (script, find_hunk, pr_context_hunk); +} + +/* Print a pair of line numbers with a comma, translated for file FILE. + If the second number is not greater, use the first in place of it. + + Args A and B are internal line numbers. + We print the translated (real) line numbers. */ + +static void +print_context_number_range (file, a, b) + struct file_data const *file; + int a, b; +{ + int trans_a, trans_b; + translate_range (file, a, b, &trans_a, &trans_b); + + /* Note: we can have B < A in the case of a range of no lines. + In this case, we should print the line number before the range, + which is B. */ + if (trans_b > trans_a) + printf_output ("%d,%d", trans_a, trans_b); + else + printf_output ("%d", trans_b); +} + +/* Print a portion of an edit script in context format. + HUNK is the beginning of the portion to be printed. + The end is marked by a `link' that has been nulled out. + + Prints out lines from both files, and precedes each + line with the appropriate flag-character. */ + +static void +pr_context_hunk (hunk) + struct change *hunk; +{ + int first0, last0, first1, last1, show_from, show_to, i; + struct change *next; + char const *prefix; + char const *function; + size_t function_length; + + /* Determine range of line numbers involved in each file. */ + + analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to); + + if (!show_from && !show_to) + return; + + /* Include a context's width before and after. */ + + i = - files[0].prefix_lines; + first0 = max (first0 - context, i); + first1 = max (first1 - context, i); + last0 = min (last0 + context, files[0].valid_lines - 1); + last1 = min (last1 + context, files[1].valid_lines - 1); + + /* If desired, find the preceding function definition line in file 0. */ + function = 0; + if (function_regexp_list) + find_function (&files[0], first0, &function, &function_length); + + begin_output (); + + /* If we looked for and found a function this is part of, + include its name in the header of the diff section. */ + printf_output ("***************"); + + if (function) + { + printf_output (" "); + write_output (function, min (function_length - 1, 40)); + } + + printf_output ("\n*** "); + print_context_number_range (&files[0], first0, last0); + printf_output (" ****\n"); + + if (show_from) + { + next = hunk; + + for (i = first0; i <= last0; i++) + { + /* Skip past changes that apply (in file 0) + only to lines before line I. */ + + while (next && next->line0 + next->deleted <= i) + next = next->link; + + /* Compute the marking for line I. */ + + prefix = " "; + if (next && next->line0 <= i) + /* The change NEXT covers this line. + If lines were inserted here in file 1, this is "changed". + Otherwise it is "deleted". */ + prefix = (next->inserted > 0 ? "!" : "-"); + + print_1_line (prefix, &files[0].linbuf[i]); + } + } + + printf_output ("--- "); + print_context_number_range (&files[1], first1, last1); + printf_output (" ----\n"); + + if (show_to) + { + next = hunk; + + for (i = first1; i <= last1; i++) + { + /* Skip past changes that apply (in file 1) + only to lines before line I. */ + + while (next && next->line1 + next->inserted <= i) + next = next->link; + + /* Compute the marking for line I. */ + + prefix = " "; + if (next && next->line1 <= i) + /* The change NEXT covers this line. + If lines were deleted here in file 0, this is "changed". + Otherwise it is "inserted". */ + prefix = (next->deleted > 0 ? "!" : "+"); + + print_1_line (prefix, &files[1].linbuf[i]); + } + } +} + +/* Print a pair of line numbers with a comma, translated for file FILE. + If the second number is smaller, use the first in place of it. + If the numbers are equal, print just one number. + + Args A and B are internal line numbers. + We print the translated (real) line numbers. */ + +static void +print_unidiff_number_range (file, a, b) + struct file_data const *file; + int a, b; +{ + int trans_a, trans_b; + translate_range (file, a, b, &trans_a, &trans_b); + + /* Note: we can have B < A in the case of a range of no lines. + In this case, we should print the line number before the range, + which is B. */ + if (trans_b <= trans_a) + printf_output (trans_b == trans_a ? "%d" : "%d,0", trans_b); + else + printf_output ("%d,%d", trans_a, trans_b - trans_a + 1); +} + +/* Print a portion of an edit script in unidiff format. + HUNK is the beginning of the portion to be printed. + The end is marked by a `link' that has been nulled out. + + Prints out lines from both files, and precedes each + line with the appropriate flag-character. */ + +static void +pr_unidiff_hunk (hunk) + struct change *hunk; +{ + int first0, last0, first1, last1, show_from, show_to, i, j, k; + struct change *next; + char const *function; + size_t function_length; + + /* Determine range of line numbers involved in each file. */ + + analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to); + + if (!show_from && !show_to) + return; + + /* Include a context's width before and after. */ + + i = - files[0].prefix_lines; + first0 = max (first0 - context, i); + first1 = max (first1 - context, i); + last0 = min (last0 + context, files[0].valid_lines - 1); + last1 = min (last1 + context, files[1].valid_lines - 1); + + /* If desired, find the preceding function definition line in file 0. */ + function = 0; + if (function_regexp_list) + find_function (&files[0], first0, &function, &function_length); + + begin_output (); + + printf_output ("@@ -"); + print_unidiff_number_range (&files[0], first0, last0); + printf_output (" +"); + print_unidiff_number_range (&files[1], first1, last1); + printf_output (" @@"); + + /* If we looked for and found a function this is part of, + include its name in the header of the diff section. */ + + if (function) + { + write_output (" ", 1); + write_output (function, min (function_length - 1, 40)); + } + write_output ("\n", 1); + + next = hunk; + i = first0; + j = first1; + + while (i <= last0 || j <= last1) + { + + /* If the line isn't a difference, output the context from file 0. */ + + if (!next || i < next->line0) + { + write_output (tab_align_flag ? "\t" : " ", 1); + print_1_line (0, &files[0].linbuf[i++]); + j++; + } + else + { + /* For each difference, first output the deleted part. */ + + k = next->deleted; + while (k--) + { + write_output ("-", 1); + if (tab_align_flag) + write_output ("\t", 1); + print_1_line (0, &files[0].linbuf[i++]); + } + + /* Then output the inserted part. */ + + k = next->inserted; + while (k--) + { + write_output ("+", 1); + if (tab_align_flag) + write_output ("\t", 1); + print_1_line (0, &files[1].linbuf[j++]); + } + + /* We're done with this hunk, so on to the next! */ + + next = next->link; + } + } +} + +/* Scan a (forward-ordered) edit script for the first place that more than + 2*CONTEXT unchanged lines appear, and return a pointer + to the `struct change' for the last change before those lines. */ + +static struct change * +find_hunk (start) + struct change *start; +{ + struct change *prev; + int top0, top1; + int thresh; + + do + { + /* Compute number of first line in each file beyond this changed. */ + top0 = start->line0 + start->deleted; + top1 = start->line1 + start->inserted; + prev = start; + start = start->link; + /* Threshold distance is 2*CONTEXT between two non-ignorable changes, + but only CONTEXT if one is ignorable. */ + thresh = ((prev->ignore || (start && start->ignore)) + ? context + : 2 * context + 1); + /* It is not supposed to matter which file we check in the end-test. + If it would matter, crash. */ + if (start && start->line0 - top0 != start->line1 - top1) + abort (); + } while (start + /* Keep going if less than THRESH lines + elapse before the affected line. */ + && start->line0 < top0 + thresh); + + return prev; +} + +/* Set the `ignore' flag properly in each change in SCRIPT. + It should be 1 if all the lines inserted or deleted in that change + are ignorable lines. */ + +static void +mark_ignorable (script) + struct change *script; +{ + while (script) + { + struct change *next = script->link; + int first0, last0, first1, last1, deletes, inserts; + + /* Turn this change into a hunk: detach it from the others. */ + script->link = 0; + + /* Determine whether this change is ignorable. */ + analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts); + /* Reconnect the chain as before. */ + script->link = next; + + /* If the change is ignorable, mark it. */ + script->ignore = (!deletes && !inserts); + + /* Advance to the following change. */ + script = next; + } +} + +/* Find the last function-header line in FILE prior to line number LINENUM. + This is a line containing a match for the regexp in `function_regexp'. + Store the address of the line text into LINEP and the length of the + line into LENP. + Do not store anything if no function-header is found. */ + +static void +find_function (file, linenum, linep, lenp) + struct file_data const *file; + int linenum; + char const **linep; + size_t *lenp; +{ + int i = linenum; + int last = find_function_last_search; + find_function_last_search = i; + + while (--i >= last) + { + /* See if this line is what we want. */ + struct regexp_list *r; + char const *line = file->linbuf[i]; + size_t len = file->linbuf[i + 1] - line; + + for (r = function_regexp_list; r; r = r->next) + if (0 <= re_search (&r->buf, line, len, 0, len, 0)) + { + *linep = line; + *lenp = len; + find_function_last_match = i; + return; + } + } + /* If we search back to where we started searching the previous time, + find the line we found last time. */ + if (find_function_last_match >= - file->prefix_lines) + { + i = find_function_last_match; + *linep = file->linbuf[i]; + *lenp = file->linbuf[i + 1] - *linep; + return; + } + return; +} diff --git a/contrib/cvs-1.12.9/diff/diff.c b/contrib/cvs-1.12.9/diff/diff.c new file mode 100644 index 0000000000..c1324c66d3 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/diff.c @@ -0,0 +1,1266 @@ +/* GNU DIFF entry routine. + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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. + +*/ + +/* GNU DIFF was written by Mike Haertel, David Hayes, + Richard Stallman, Len Tower, and Paul Eggert. */ + +#define GDIFF_MAIN +#include "diff.h" +#include +#include "getopt.h" + +#ifdef HAVE_FNMATCH +# include /* This is supposed to be available on Posix systems */ +#else /* HAVE_FNMATCH */ +# include "fnmatch.h" /* Our substitute */ +#endif /* HAVE_FNMATCH */ + +#ifndef DEFAULT_WIDTH +#define DEFAULT_WIDTH 130 +#endif + +#ifndef GUTTER_WIDTH_MINIMUM +#define GUTTER_WIDTH_MINIMUM 3 +#endif + +/* diff.c has a real initialize_main function. */ +#ifdef initialize_main +#undef initialize_main +#endif + +static char const *filetype PARAMS((struct stat const *)); +static char *option_list PARAMS((char **, int)); +static int add_exclude_file PARAMS((char const *)); +static int ck_atoi PARAMS((char const *, int *)); +static int compare_files PARAMS((char const *, char const *, char const *, char const *, int)); +static int specify_format PARAMS((char **, char *)); +static void add_exclude PARAMS((char const *)); +static void add_regexp PARAMS((struct regexp_list **, char const *)); +static void specify_style PARAMS((enum output_style)); +static int try_help PARAMS((char const *)); +static void check_output PARAMS((FILE *)); +static void usage PARAMS((void)); +static void initialize_main PARAMS((int *, char ***)); + +/* Nonzero for -r: if comparing two directories, + compare their common subdirectories recursively. */ + +static int recursive; + +/* For debugging: don't do discard_confusing_lines. */ + +int no_discards; + +#if HAVE_SETMODE +/* I/O mode: nonzero only if using binary input/output. */ +static int binary_I_O; +#endif + +/* Return a string containing the command options with which diff was invoked. + Spaces appear between what were separate ARGV-elements. + There is a space at the beginning but none at the end. + If there were no options, the result is an empty string. + + Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT, + the length of that vector. */ + +static char * +option_list (optionvec, count) + char **optionvec; /* Was `vector', but that collides on Alliant. */ + int count; +{ + int i; + size_t length = 0; + char *result; + + for (i = 0; i < count; i++) + length += strlen (optionvec[i]) + 1; + + result = xmalloc (length + 1); + result[0] = 0; + + for (i = 0; i < count; i++) + { + strcat (result, " "); + strcat (result, optionvec[i]); + } + + return result; +} + +/* Convert STR to a positive integer, storing the result in *OUT. + If STR is not a valid integer, return -1 (otherwise 0). */ +static int +ck_atoi (str, out) + char const *str; + int *out; +{ + char const *p; + for (p = str; *p; p++) + if (*p < '0' || *p > '9') + return -1; + + *out = atoi (optarg); + return 0; +} + +/* Keep track of excluded file name patterns. */ + +static char const **exclude; +static int exclude_alloc, exclude_count; + +int +excluded_filename (f) + char const *f; +{ + int i; + for (i = 0; i < exclude_count; i++) + if (fnmatch (exclude[i], f, 0) == 0) + return 1; + return 0; +} + +static void +add_exclude (pattern) + char const *pattern; +{ + if (exclude_alloc <= exclude_count) + exclude = (char const **) + (exclude_alloc == 0 + ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude)) + : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude))); + + exclude[exclude_count++] = pattern; +} + +static int +add_exclude_file (name) + char const *name; +{ + struct file_data f; + char *p, *q, *lim; + + f.name = optarg; + f.desc = (strcmp (optarg, "-") == 0 + ? STDIN_FILENO + : open (optarg, O_RDONLY, 0)); + if (f.desc < 0 || fstat (f.desc, &f.stat) != 0) + return -1; + + sip (&f, 1); + slurp (&f); + + for (p = f.buffer, lim = p + f.buffered_chars; p < lim; p = q) + { + q = (char *) memchr (p, '\n', lim - p); + if (!q) + q = lim; + *q++ = 0; + add_exclude (p); + } + + return close (f.desc); +} + +/* The numbers 129- that appear in the fourth element of some entries + tell the big switch in `diff_run' how to process those options. */ + +static struct option const longopts[] = +{ + {"ignore-blank-lines", 0, 0, 'B'}, + {"context", 2, 0, 'C'}, + {"ifdef", 1, 0, 'D'}, + {"show-function-line", 1, 0, 'F'}, + {"speed-large-files", 0, 0, 'H'}, + {"ignore-matching-lines", 1, 0, 'I'}, + {"label", 1, 0, 'L'}, + {"file-label", 1, 0, 'L'}, /* An alias, no longer recommended */ + {"new-file", 0, 0, 'N'}, + {"entire-new-file", 0, 0, 'N'}, /* An alias, no longer recommended */ + {"unidirectional-new-file", 0, 0, 'P'}, + {"starting-file", 1, 0, 'S'}, + {"initial-tab", 0, 0, 'T'}, + {"width", 1, 0, 'W'}, + {"text", 0, 0, 'a'}, + {"ascii", 0, 0, 'a'}, /* An alias, no longer recommended */ + {"ignore-space-change", 0, 0, 'b'}, + {"minimal", 0, 0, 'd'}, + {"ed", 0, 0, 'e'}, + {"forward-ed", 0, 0, 'f'}, + {"ignore-case", 0, 0, 'i'}, + {"paginate", 0, 0, 'l'}, + {"print", 0, 0, 'l'}, /* An alias, no longer recommended */ + {"rcs", 0, 0, 'n'}, + {"show-c-function", 0, 0, 'p'}, + {"brief", 0, 0, 'q'}, + {"recursive", 0, 0, 'r'}, + {"report-identical-files", 0, 0, 's'}, + {"expand-tabs", 0, 0, 't'}, + {"version", 0, 0, 'v'}, + {"ignore-all-space", 0, 0, 'w'}, + {"exclude", 1, 0, 'x'}, + {"exclude-from", 1, 0, 'X'}, + {"side-by-side", 0, 0, 'y'}, + {"unified", 2, 0, 'U'}, + {"left-column", 0, 0, 129}, + {"suppress-common-lines", 0, 0, 130}, + {"sdiff-merge-assist", 0, 0, 131}, + {"old-line-format", 1, 0, 132}, + {"new-line-format", 1, 0, 133}, + {"unchanged-line-format", 1, 0, 134}, + {"line-format", 1, 0, 135}, + {"old-group-format", 1, 0, 136}, + {"new-group-format", 1, 0, 137}, + {"unchanged-group-format", 1, 0, 138}, + {"changed-group-format", 1, 0, 139}, + {"horizon-lines", 1, 0, 140}, + {"help", 0, 0, 141}, + {"binary", 0, 0, 142}, + {0, 0, 0, 0} +}; + + + +int +diff_run (argc, argv, out, callbacks_arg) + int argc; + char *argv[]; + const char *out; + const struct diff_callbacks *callbacks_arg; +{ + int val; + int c; + int prev = -1; + int width = DEFAULT_WIDTH; + int show_c_function = 0; + int optind_old; + int opened_file = 0; + + callbacks = callbacks_arg; + + /* Do our initializations. */ + initialize_main (&argc, &argv); + optind_old = optind; + optind = 0; + + /* Set the jump buffer, so that diff may abort execution without + terminating the process. */ + val = setjmp (diff_abort_buf); + if (val != 0) + { + optind = optind_old; + if (opened_file) + fclose (outfile); + return val; + } + + /* Decode the options. */ + while ((c = getopt_long (argc, argv, + "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y", + longopts, 0)) != EOF) + { + switch (c) + { + /* All digits combine in decimal to specify the context-size. */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '0': + if (context == -1) + context = 0; + /* If a context length has already been specified, + more digits allowed only if they follow right after the others. + Reject two separate runs of digits, or digits after -C. */ + else if (prev < '0' || prev > '9') + fatal ("context length specified twice"); + + context = context * 10 + c - '0'; + break; + + case 'a': + /* Treat all files as text files; never treat as binary. */ + always_text_flag = 1; + break; + + case 'b': + /* Ignore changes in amount of white space. */ + ignore_space_change_flag = 1; + ignore_some_changes = 1; + ignore_some_line_changes = 1; + break; + + case 'B': + /* Ignore changes affecting only blank lines. */ + ignore_blank_lines_flag = 1; + ignore_some_changes = 1; + break; + + case 'C': /* +context[=lines] */ + case 'U': /* +unified[=lines] */ + if (optarg) + { + if (context >= 0) + fatal ("context length specified twice"); + + if (ck_atoi (optarg, &context)) + fatal ("invalid context length argument"); + } + + /* Falls through. */ + case 'c': + /* Make context-style output. */ + specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); + break; + + case 'd': + /* Don't discard lines. This makes things slower (sometimes much + slower) but will find a guaranteed minimal set of changes. */ + no_discards = 1; + break; + + case 'D': + /* Make merged #ifdef output. */ + specify_style (OUTPUT_IFDEF); + { + int i, err = 0; + static char const C_ifdef_group_formats[] = + "#ifndef %s\n%%<#endif /* not %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c%%=%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n"; + char *b = xmalloc (sizeof (C_ifdef_group_formats) + + 7 * strlen(optarg) - 14 /* 7*"%s" */ + - 8 /* 5*"%%" + 3*"%c" */); + sprintf (b, C_ifdef_group_formats, + optarg, optarg, 0, + optarg, optarg, 0, 0, + optarg, optarg, optarg); + for (i = 0; i < 4; i++) + { + err |= specify_format (&group_format[i], b); + b += strlen (b) + 1; + } + if (err) + diff_error ("conflicting #ifdef formats", 0, 0); + } + break; + + case 'e': + /* Make output that is a valid `ed' script. */ + specify_style (OUTPUT_ED); + break; + + case 'f': + /* Make output that looks vaguely like an `ed' script + but has changes in the order they appear in the file. */ + specify_style (OUTPUT_FORWARD_ED); + break; + + case 'F': + /* Show, for each set of changes, the previous line that + matches the specified regexp. Currently affects only + context-style output. */ + add_regexp (&function_regexp_list, optarg); + break; + + case 'h': + /* Split the files into chunks of around 1500 lines + for faster processing. Usually does not change the result. + + This currently has no effect. */ + break; + + case 'H': + /* Turn on heuristics that speed processing of large files + with a small density of changes. */ + heuristic = 1; + break; + + case 'i': + /* Ignore changes in case. */ + ignore_case_flag = 1; + ignore_some_changes = 1; + ignore_some_line_changes = 1; + break; + + case 'I': + /* Ignore changes affecting only lines that match the + specified regexp. */ + add_regexp (&ignore_regexp_list, optarg); + ignore_some_changes = 1; + break; + + case 'l': + /* Pass the output through `pr' to paginate it. */ + paginate_flag = 1; +#if !defined(SIGCHLD) && defined(SIGCLD) +#define SIGCHLD SIGCLD +#endif +#ifdef SIGCHLD + /* Pagination requires forking and waiting, and + System V fork+wait does not work if SIGCHLD is ignored. */ + signal (SIGCHLD, SIG_DFL); +#endif + break; + + case 'L': + /* Specify file labels for `-c' output headers. */ + if (!file_label[0]) + file_label[0] = optarg; + else if (!file_label[1]) + file_label[1] = optarg; + else + fatal ("too many file label options"); + break; + + case 'n': + /* Output RCS-style diffs, like `-f' except that each command + specifies the number of lines affected. */ + specify_style (OUTPUT_RCS); + break; + + case 'N': + /* When comparing directories, if a file appears only in one + directory, treat it as present but empty in the other. */ + entire_new_file_flag = 1; + break; + + case 'p': + /* Make context-style output and show name of last C function. */ + show_c_function = 1; + add_regexp (&function_regexp_list, "^[_a-zA-Z$]"); + break; + + case 'P': + /* When comparing directories, if a file appears only in + the second directory of the two, + treat it as present but empty in the other. */ + unidirectional_new_file_flag = 1; + break; + + case 'q': + no_details_flag = 1; + break; + + case 'r': + /* When comparing directories, + recursively compare any subdirectories found. */ + recursive = 1; + break; + + case 's': + /* Print a message if the files are the same. */ + print_file_same_flag = 1; + break; + + case 'S': + /* When comparing directories, start with the specified + file name. This is used for resuming an aborted comparison. */ + dir_start_file = optarg; + break; + + case 't': + /* Expand tabs to spaces in the output so that it preserves + the alignment of the input files. */ + tab_expand_flag = 1; + break; + + case 'T': + /* Use a tab in the output, rather than a space, before the + text of an input line, so as to keep the proper alignment + in the input line without changing the characters in it. */ + tab_align_flag = 1; + break; + + case 'u': + /* Output the context diff in unidiff format. */ + specify_style (OUTPUT_UNIFIED); + break; + + case 'v': + if (callbacks && callbacks->write_stdout) + { + (*callbacks->write_stdout) ("diff - GNU diffutils version "); + (*callbacks->write_stdout) (diff_version_string); + (*callbacks->write_stdout) ("\n"); + } + else + printf ("diff - GNU diffutils version %s\n", diff_version_string); + return 0; + + case 'w': + /* Ignore horizontal white space when comparing lines. */ + ignore_all_space_flag = 1; + ignore_some_changes = 1; + ignore_some_line_changes = 1; + break; + + case 'x': + add_exclude (optarg); + break; + + case 'X': + if (add_exclude_file (optarg) != 0) + pfatal_with_name (optarg); + break; + + case 'y': + /* Use side-by-side (sdiff-style) columnar output. */ + specify_style (OUTPUT_SDIFF); + break; + + case 'W': + /* Set the line width for OUTPUT_SDIFF. */ + if (ck_atoi (optarg, &width) || width <= 0) + fatal ("column width must be a positive integer"); + break; + + case 129: + sdiff_left_only = 1; + break; + + case 130: + sdiff_skip_common_lines = 1; + break; + + case 131: + /* sdiff-style columns output. */ + specify_style (OUTPUT_SDIFF); + sdiff_help_sdiff = 1; + break; + + case 132: + case 133: + case 134: + specify_style (OUTPUT_IFDEF); + if (specify_format (&line_format[c - 132], optarg) != 0) + diff_error ("conflicting line format", 0, 0); + break; + + case 135: + specify_style (OUTPUT_IFDEF); + { + int i, err = 0; + for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) + err |= specify_format (&line_format[i], optarg); + if (err) + diff_error ("conflicting line format", 0, 0); + } + break; + + case 136: + case 137: + case 138: + case 139: + specify_style (OUTPUT_IFDEF); + if (specify_format (&group_format[c - 136], optarg) != 0) + diff_error ("conflicting group format", 0, 0); + break; + + case 140: + if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0) + fatal ("horizon must be a nonnegative integer"); + break; + + case 141: + usage (); + if (! callbacks || ! callbacks->write_stdout) + check_output (stdout); + return 0; + + case 142: + /* Use binary I/O when reading and writing data. + On Posix hosts, this has no effect. */ +#if HAVE_SETMODE + binary_I_O = 1; +# if 0 + /* Because this code is leftover from pre-library days, + there is no way to set stdout back to the default mode + when we are done. As it turns out, I think the only + parts of CVS that pass out == NULL, and thus cause diff + to write to stdout, are "cvs diff" and "cvs rdiff". So + I'm not going to worry about this too much yet. */ + setmode (STDOUT_FILENO, O_BINARY); +# else + if (out == NULL) + error (0, 0, "warning: did not set stdout to binary mode"); +# endif +#endif + break; + + default: + return try_help (0); + } + prev = c; + } + + if (argc - optind != 2) + return try_help (argc - optind < 2 ? "missing operand" : "extra operand"); + + { + /* + * We maximize first the half line width, and then the gutter width, + * according to the following constraints: + * 1. Two half lines plus a gutter must fit in a line. + * 2. If the half line width is nonzero: + * a. The gutter width is at least GUTTER_WIDTH_MINIMUM. + * b. If tabs are not expanded to spaces, + * a half line plus a gutter is an integral number of tabs, + * so that tabs in the right column line up. + */ + int t = tab_expand_flag ? 1 : TAB_WIDTH; + int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t) * t; + sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)), + sdiff_column2_offset = sdiff_half_width ? off : width; + } + + if (show_c_function && output_style != OUTPUT_UNIFIED) + specify_style (OUTPUT_CONTEXT); + + if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED) + context = 0; + else if (context == -1) + /* Default amount of context for -c. */ + context = 3; + + if (output_style == OUTPUT_IFDEF) + { + /* Format arrays are char *, not char const *, + because integer formats are temporarily modified. + But it is safe to assign a constant like "%=" to a format array, + since "%=" does not format any integers. */ + int i; + for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) + if (!line_format[i]) + line_format[i] = "%l\n"; + if (!group_format[OLD]) + group_format[OLD] + = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%<"; + if (!group_format[NEW]) + group_format[NEW] + = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%>"; + if (!group_format[UNCHANGED]) + group_format[UNCHANGED] = "%="; + if (!group_format[CHANGED]) + group_format[CHANGED] = concat (group_format[OLD], + group_format[NEW], ""); + } + + no_diff_means_no_output = + (output_style == OUTPUT_IFDEF ? + (!*group_format[UNCHANGED] + || (strcmp (group_format[UNCHANGED], "%=") == 0 + && !*line_format[UNCHANGED])) + : output_style == OUTPUT_SDIFF ? sdiff_skip_common_lines : 1); + + switch_string = option_list (argv + 1, optind - 1); + + if (callbacks && callbacks->write_output) + { + if (out != NULL) + { + diff_error ("write callback with output file", 0, 0); + return 2; + } + } + else + { + if (out == NULL) + outfile = stdout; + else + { +#if HAVE_SETMODE + /* A diff which is full of ^Z and such isn't going to work + very well in text mode. */ + if (binary_I_O) + outfile = fopen (out, "wb"); + else +#endif + outfile = fopen (out, "w"); + if (outfile == NULL) + { + perror_with_name ("could not open output file"); + return 2; + } + opened_file = 1; + } + } + + val = compare_files (0, argv[optind], 0, argv[optind + 1], 0); + + /* Print any messages that were saved up for last. */ + print_message_queue (); + + free (switch_string); + + optind = optind_old; + + if (! callbacks || ! callbacks->write_output) + check_output (outfile); + + if (opened_file) + if (fclose (outfile) != 0) + perror_with_name ("close error on output file"); + + return val; +} + +/* Add the compiled form of regexp PATTERN to REGLIST. */ + +static void +add_regexp (reglist, pattern) + struct regexp_list **reglist; + char const *pattern; +{ + struct regexp_list *r; + char const *m; + + r = (struct regexp_list *) xmalloc (sizeof (*r)); + bzero (r, sizeof (*r)); + r->buf.fastmap = xmalloc (256); + m = re_compile_pattern (pattern, strlen (pattern), &r->buf); + if (m != 0) + diff_error ("%s: %s", pattern, m); + + /* Add to the start of the list, since it's easier than the end. */ + r->next = *reglist; + *reglist = r; +} + +static int +try_help (reason) + char const *reason; +{ + if (reason) + diff_error ("%s", reason, 0); + diff_error ("Try `%s --help' for more information.", diff_program_name, 0); + return 2; +} + +static void +check_output (file) + FILE *file; +{ + if (ferror (file) || fflush (file) != 0) + fatal ("write error"); +} + +static char const * const option_help[] = { +"-i --ignore-case Consider upper- and lower-case to be the same.", +"-w --ignore-all-space Ignore all white space.", +"-b --ignore-space-change Ignore changes in the amount of white space.", +"-B --ignore-blank-lines Ignore changes whose lines are all blank.", +"-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.", +#if HAVE_SETMODE +"--binary Read and write data in binary mode.", +#endif +"-a --text Treat all files as text.\n", +"-c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.", +"-u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.", +" -NUM Use NUM context lines.", +" -L LABEL --label LABEL Use LABEL instead of file name.", +" -p --show-c-function Show which C function each change is in.", +" -F RE --show-function-line=RE Show the most recent line matching RE.", +"-q --brief Output only whether files differ.", +"-e --ed Output an ed script.", +"-n --rcs Output an RCS format diff.", +"-y --side-by-side Output in two columns.", +" -W NUM --width=NUM Output at most NUM (default 130) characters per line.", +" --left-column Output only the left column of common lines.", +" --suppress-common-lines Do not output common lines.", +"-DNAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.", +"--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.", +"--line-format=LFMT Similar, but format all input lines with LFMT.", +"--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.", +" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.", +" GFMT may contain:", +" %< lines from FILE1", +" %> lines from FILE2", +" %= lines common to FILE1 and FILE2", +" %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER", +" LETTERs are as follows for new group, lower case for old group:", +" F first line number", +" L last line number", +" N number of lines = L-F+1", +" E F-1", +" M L+1", +" LFMT may contain:", +" %L contents of line", +" %l contents of line, excluding any trailing newline", +" %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number", +" Either GFMT or LFMT may contain:", +" %% %", +" %c'C' the single character C", +" %c'\\OOO' the character with octal code OOO\n", +"-l --paginate Pass the output through `pr' to paginate it.", +"-t --expand-tabs Expand tabs to spaces in output.", +"-T --initial-tab Make tabs line up by prepending a tab.\n", +"-r --recursive Recursively compare any subdirectories found.", +"-N --new-file Treat absent files as empty.", +"-P --unidirectional-new-file Treat absent first files as empty.", +"-s --report-identical-files Report when two files are the same.", +"-x PAT --exclude=PAT Exclude files that match PAT.", +"-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE.", +"-S FILE --starting-file=FILE Start with FILE when comparing directories.\n", +"--horizon-lines=NUM Keep NUM lines of the common prefix and suffix.", +"-d --minimal Try hard to find a smaller set of changes.", +"-H --speed-large-files Assume large files and many scattered small changes.\n", +"-v --version Output version info.", +"--help Output this help.", +0 +}; + +static void +usage () +{ + char const * const *p; + + if (callbacks && callbacks->write_stdout) + { + (*callbacks->write_stdout) ("Usage: "); + (*callbacks->write_stdout) (diff_program_name); + (*callbacks->write_stdout) (" [OPTION]... FILE1 FILE2\n\n"); + for (p = option_help; *p; p++) + { + (*callbacks->write_stdout) (" "); + (*callbacks->write_stdout) (*p); + (*callbacks->write_stdout) ("\n"); + } + (*callbacks->write_stdout) + ("\nIf FILE1 or FILE2 is `-', read standard input.\n"); + } + else + { + printf ("Usage: %s [OPTION]... FILE1 FILE2\n\n", diff_program_name); + for (p = option_help; *p; p++) + printf (" %s\n", *p); + printf ("\nIf FILE1 or FILE2 is `-', read standard input.\n"); + } +} + +static int +specify_format (var, value) + char **var; + char *value; +{ + int err = *var ? strcmp (*var, value) : 0; + *var = value; + return err; +} + +static void +specify_style (style) + enum output_style style; +{ + if (output_style != OUTPUT_NORMAL + && output_style != style) + diff_error ("conflicting specifications of output style", 0, 0); + output_style = style; +} + +static char const * +filetype (st) + struct stat const *st; +{ + /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats. + To keep diagnostics grammatical, the returned string must start + with a consonant. */ + + if (S_ISREG (st->st_mode)) + { + if (st->st_size == 0) + return "regular empty file"; + /* Posix.2 section 5.14.2 seems to suggest that we must read the file + and guess whether it's C, Fortran, etc., but this is somewhat useless + and doesn't reflect historical practice. We're allowed to guess + wrong, so we don't bother to read the file. */ + return "regular file"; + } + if (S_ISDIR (st->st_mode)) return "directory"; + + /* other Posix.1 file types */ +#ifdef S_ISBLK + if (S_ISBLK (st->st_mode)) return "block special file"; +#endif +#ifdef S_ISCHR + if (S_ISCHR (st->st_mode)) return "character special file"; +#endif +#ifdef S_ISFIFO + if (S_ISFIFO (st->st_mode)) return "fifo"; +#endif + + /* other Posix.1b file types */ +#ifdef S_TYPEISMQ + if (S_TYPEISMQ (st)) return "message queue"; +#endif +#ifdef S_TYPEISSEM + if (S_TYPEISSEM (st)) return "semaphore"; +#endif +#ifdef S_TYPEISSHM + if (S_TYPEISSHM (st)) return "shared memory object"; +#endif + + /* other popular file types */ + /* S_ISLNK is impossible with `fstat' and `stat'. */ +#ifdef S_ISSOCK + if (S_ISSOCK (st->st_mode)) return "socket"; +#endif + + return "weird file"; +} + +/* Compare two files (or dirs) with specified names + DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion. + (if DIR0 is 0, then the name is just NAME0, etc.) + This is self-contained; it opens the files and closes them. + + Value is 0 if files are the same, 1 if different, + 2 if there is a problem opening them. */ + +static int +compare_files (dir0, name0, dir1, name1, depth) + char const *dir0, *dir1; + char const *name0, *name1; + int depth; +{ + struct file_data inf[2]; + register int i; + int val; + int same_files; + int failed = 0; + char *free0 = 0, *free1 = 0; + + /* If this is directory comparison, perhaps we have a file + that exists only in one of the directories. + If so, just print a message to that effect. */ + + if (! ((name0 != 0 && name1 != 0) + || (unidirectional_new_file_flag && name1 != 0) + || entire_new_file_flag)) + { + char const *name = name0 == 0 ? name1 : name0; + char const *dir = name0 == 0 ? dir1 : dir0; + message ("Only in %s: %s\n", dir, name); + /* Return 1 so that diff_dirs will return 1 ("some files differ"). */ + return 1; + } + + bzero (inf, sizeof (inf)); + + /* Mark any nonexistent file with -1 in the desc field. */ + /* Mark unopened files (e.g. directories) with -2. */ + + inf[0].desc = name0 == 0 ? -1 : -2; + inf[1].desc = name1 == 0 ? -1 : -2; + + /* Now record the full name of each file, including nonexistent ones. */ + + if (name0 == 0) + name0 = name1; + if (name1 == 0) + name1 = name0; + + inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0)); + inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1)); + + /* Stat the files. Record whether they are directories. */ + + for (i = 0; i <= 1; i++) + { + if (inf[i].desc != -1) + { + int stat_result; + + if (i && filename_cmp (inf[i].name, inf[0].name) == 0) + { + inf[i].stat = inf[0].stat; + stat_result = 0; + } + else if (strcmp (inf[i].name, "-") == 0) + { + inf[i].desc = STDIN_FILENO; + stat_result = fstat (STDIN_FILENO, &inf[i].stat); + if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode)) + { + off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR); + if (pos == -1) + stat_result = -1; + else + { + if (pos <= inf[i].stat.st_size) + inf[i].stat.st_size -= pos; + else + inf[i].stat.st_size = 0; + /* Posix.2 4.17.6.1.4 requires current time for stdin. */ + time (&inf[i].stat.st_mtime); + } + } + } + else + stat_result = stat (inf[i].name, &inf[i].stat); + + if (stat_result != 0) + { + perror_with_name (inf[i].name); + failed = 1; + } + else + { + inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0; + if (inf[1 - i].desc == -1) + { + inf[1 - i].dir_p = inf[i].dir_p; + inf[1 - i].stat.st_mode = inf[i].stat.st_mode; + } + } + } + } + + if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p) + { + /* If one is a directory, and it was specified in the command line, + use the file in that dir with the other file's basename. */ + + int fnm_arg = inf[0].dir_p; + int dir_arg = 1 - fnm_arg; + char const *fnm = inf[fnm_arg].name; + char const *dir = inf[dir_arg].name; + char const *p = filename_lastdirchar (fnm); + char const *filename = inf[dir_arg].name + = dir_file_pathname (dir, p ? p + 1 : fnm); + + if (strcmp (fnm, "-") == 0) + fatal ("can't compare - to a directory"); + + if (stat (filename, &inf[dir_arg].stat) != 0) + { + perror_with_name (filename); + failed = 1; + } + else + inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode); + } + + if (failed) + { + + /* If either file should exist but does not, return 2. */ + + val = 2; + + } + else if ((same_files = inf[0].desc != -1 && inf[1].desc != -1 + && 0 < same_file (&inf[0].stat, &inf[1].stat)) + && no_diff_means_no_output) + { + /* The two named files are actually the same physical file. + We know they are identical without actually reading them. */ + + val = 0; + } + else if (inf[0].dir_p & inf[1].dir_p) + { + if (output_style == OUTPUT_IFDEF) + fatal ("-D option not supported with directories"); + + /* If both are directories, compare the files in them. */ + + if (depth > 0 && !recursive) + { + /* But don't compare dir contents one level down + unless -r was specified. */ + message ("Common subdirectories: %s and %s\n", + inf[0].name, inf[1].name); + val = 0; + } + else + { + val = diff_dirs (inf, compare_files, depth); + } + + } + else if ((inf[0].dir_p | inf[1].dir_p) + || (depth > 0 + && (! S_ISREG (inf[0].stat.st_mode) + || ! S_ISREG (inf[1].stat.st_mode)))) + { + /* Perhaps we have a subdirectory that exists only in one directory. + If so, just print a message to that effect. */ + + if (inf[0].desc == -1 || inf[1].desc == -1) + { + if ((inf[0].dir_p | inf[1].dir_p) + && recursive + && (entire_new_file_flag + || (unidirectional_new_file_flag && inf[0].desc == -1))) + val = diff_dirs (inf, compare_files, depth); + else + { + char const *dir = (inf[0].desc == -1) ? dir1 : dir0; + /* See Posix.2 section 4.17.6.1.1 for this format. */ + message ("Only in %s: %s\n", dir, name0); + val = 1; + } + } + else + { + /* We have two files that are not to be compared. */ + + /* See Posix.2 section 4.17.6.1.1 for this format. */ + message5 ("File %s is a %s while file %s is a %s\n", + inf[0].name, filetype (&inf[0].stat), + inf[1].name, filetype (&inf[1].stat)); + + /* This is a difference. */ + val = 1; + } + } + else if ((no_details_flag & ~ignore_some_changes) + && inf[0].stat.st_size != inf[1].stat.st_size + && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode)) + && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode))) + { + message ("Files %s and %s differ\n", inf[0].name, inf[1].name); + val = 1; + } + else + { + /* Both exist and neither is a directory. */ + + /* Open the files and record their descriptors. */ + + if (inf[0].desc == -2) + if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0) + { + perror_with_name (inf[0].name); + failed = 1; + } + if (inf[1].desc == -2) + { + if (same_files) + inf[1].desc = inf[0].desc; + else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0) + { + perror_with_name (inf[1].name); + failed = 1; + } + } + +#if HAVE_SETMODE + if (binary_I_O) + for (i = 0; i <= 1; i++) + if (0 <= inf[i].desc) + setmode (inf[i].desc, O_BINARY); +#endif + + /* Compare the files, if no error was found. */ + + val = failed ? 2 : diff_2_files (inf, depth); + + /* Close the file descriptors. */ + + if (inf[0].desc >= 0 && close (inf[0].desc) != 0) + { + perror_with_name (inf[0].name); + val = 2; + } + if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc + && close (inf[1].desc) != 0) + { + perror_with_name (inf[1].name); + val = 2; + } + } + + /* Now the comparison has been done, if no error prevented it, + and VAL is the value this function will return. */ + + if (val == 0 && !inf[0].dir_p) + { + if (print_file_same_flag) + message ("Files %s and %s are identical\n", + inf[0].name, inf[1].name); + } + else + flush_output (); + + if (free0) + free (free0); + if (free1) + free (free1); + + return val; +} + +/* Initialize status variables and flag variables used in libdiff, + to permit repeated calls to diff_run. */ + +static void +initialize_main (argcp, argvp) + int *argcp; + char ***argvp; +{ + /* These variables really must be reset each time diff_run is called. */ + output_style = OUTPUT_NORMAL; + context = -1; + file_label[0] = NULL; + file_label[1] = NULL; + diff_program_name = (*argvp)[0]; + outfile = NULL; + + /* Reset these also, just for safety's sake. (If one invocation turns + on ignore_case_flag, it must be turned off before diff_run is called + again. But it is possible to make many diffs before encountering + such a problem. */ + recursive = 0; + no_discards = 0; +#if HAVE_SETMODE + binary_I_O = 0; +#endif + no_diff_means_no_output = 0; + always_text_flag = 0; + horizon_lines = 0; + ignore_space_change_flag = 0; + ignore_all_space_flag = 0; + ignore_blank_lines_flag = 0; + ignore_some_line_changes = 0; + ignore_some_changes = 0; + ignore_case_flag = 0; + function_regexp_list = NULL; + ignore_regexp_list = NULL; + no_details_flag = 0; + print_file_same_flag = 0; + tab_align_flag = 0; + tab_expand_flag = 0; + dir_start_file = NULL; + entire_new_file_flag = 0; + unidirectional_new_file_flag = 0; + paginate_flag = 0; + bzero (group_format, sizeof (group_format)); + bzero (line_format, sizeof (line_format)); + sdiff_help_sdiff = 0; + sdiff_left_only = 0; + sdiff_skip_common_lines = 0; + sdiff_half_width = 0; + sdiff_column2_offset = 0; + switch_string = NULL; + heuristic = 0; + bzero (files, sizeof (files)); +} diff --git a/contrib/cvs-1.12.9/diff/diff.h b/contrib/cvs-1.12.9/diff/diff.h new file mode 100644 index 0000000000..642138d6ef --- /dev/null +++ b/contrib/cvs-1.12.9/diff/diff.h @@ -0,0 +1,354 @@ +/* Shared definitions for GNU DIFF + Copyright (C) 1988, 89, 91, 92, 93, 97, 1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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 "system.h" +#include +#include +#include "regex.h" +#include "diffrun.h" + +#define TAB_WIDTH 8 + +/* Variables for command line options */ + +#ifndef GDIFF_MAIN +#define EXTERN extern +#else +#define EXTERN +#endif + +/* The callbacks to use for output. */ +EXTERN const struct diff_callbacks *callbacks; + +enum output_style { + /* Default output style. */ + OUTPUT_NORMAL, + /* Output the differences with lines of context before and after (-c). */ + OUTPUT_CONTEXT, + /* Output the differences in a unified context diff format (-u). */ + OUTPUT_UNIFIED, + /* Output the differences as commands suitable for `ed' (-e). */ + OUTPUT_ED, + /* Output the diff as a forward ed script (-f). */ + OUTPUT_FORWARD_ED, + /* Like -f, but output a count of changed lines in each "command" (-n). */ + OUTPUT_RCS, + /* Output merged #ifdef'd file (-D). */ + OUTPUT_IFDEF, + /* Output sdiff style (-y). */ + OUTPUT_SDIFF +}; + +/* True for output styles that are robust, + i.e. can handle a file that ends in a non-newline. */ +#define ROBUST_OUTPUT_STYLE(S) ((S) != OUTPUT_ED && (S) != OUTPUT_FORWARD_ED) + +EXTERN enum output_style output_style; + +/* Nonzero if output cannot be generated for identical files. */ +EXTERN int no_diff_means_no_output; + +/* Number of lines of context to show in each set of diffs. + This is zero when context is not to be shown. */ +EXTERN int context; + +/* Consider all files as text files (-a). + Don't interpret codes over 0177 as implying a "binary file". */ +EXTERN int always_text_flag; + +/* Number of lines to keep in identical prefix and suffix. */ +EXTERN int horizon_lines; + +/* Ignore changes in horizontal white space (-b). */ +EXTERN int ignore_space_change_flag; + +/* Ignore all horizontal white space (-w). */ +EXTERN int ignore_all_space_flag; + +/* Ignore changes that affect only blank lines (-B). */ +EXTERN int ignore_blank_lines_flag; + +/* 1 if lines may match even if their contents do not match exactly. + This depends on various options. */ +EXTERN int ignore_some_line_changes; + +/* 1 if files may match even if their contents are not byte-for-byte identical. + This depends on various options. */ +EXTERN int ignore_some_changes; + +/* Ignore differences in case of letters (-i). */ +EXTERN int ignore_case_flag; + +/* File labels for `-c' output headers (-L). */ +EXTERN char *file_label[2]; + +struct regexp_list +{ + struct re_pattern_buffer buf; + struct regexp_list *next; +}; + +/* Regexp to identify function-header lines (-F). */ +EXTERN struct regexp_list *function_regexp_list; + +/* Ignore changes that affect only lines matching this regexp (-I). */ +EXTERN struct regexp_list *ignore_regexp_list; + +/* Say only whether files differ, not how (-q). */ +EXTERN int no_details_flag; + +/* Report files compared that match (-s). + Normally nothing is output when that happens. */ +EXTERN int print_file_same_flag; + +/* Output the differences with exactly 8 columns added to each line + so that any tabs in the text line up properly (-T). */ +EXTERN int tab_align_flag; + +/* Expand tabs in the output so the text lines up properly + despite the characters added to the front of each line (-t). */ +EXTERN int tab_expand_flag; + +/* In directory comparison, specify file to start with (-S). + All file names less than this name are ignored. */ +EXTERN char *dir_start_file; + +/* If a file is new (appears in only one dir) + include its entire contents (-N). + Then `patch' would create the file with appropriate contents. */ +EXTERN int entire_new_file_flag; + +/* If a file is new (appears in only the second dir) + include its entire contents (-P). + Then `patch' would create the file with appropriate contents. */ +EXTERN int unidirectional_new_file_flag; + +/* Pipe each file's output through pr (-l). */ +EXTERN int paginate_flag; + +enum line_class { + /* Lines taken from just the first file. */ + OLD, + /* Lines taken from just the second file. */ + NEW, + /* Lines common to both files. */ + UNCHANGED, + /* A hunk containing both old and new lines (line groups only). */ + CHANGED +}; + +/* Line group formats for old, new, unchanged, and changed groups. */ +EXTERN char *group_format[CHANGED + 1]; + +/* Line formats for old, new, and unchanged lines. */ +EXTERN char *line_format[UNCHANGED + 1]; + +/* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */ +EXTERN int sdiff_help_sdiff; + +/* Tell OUTPUT_SDIFF to show only the left version of common lines. */ +EXTERN int sdiff_left_only; + +/* Tell OUTPUT_SDIFF to not show common lines. */ +EXTERN int sdiff_skip_common_lines; + +/* The half line width and column 2 offset for OUTPUT_SDIFF. */ +EXTERN unsigned sdiff_half_width; +EXTERN unsigned sdiff_column2_offset; + +/* String containing all the command options diff received, + with spaces between and at the beginning but none at the end. + If there were no options given, this string is empty. */ +EXTERN char * switch_string; + +/* Nonzero means use heuristics for better speed. */ +EXTERN int heuristic; + +/* Name of program the user invoked (for error messages). */ +EXTERN char *diff_program_name; + +/* Jump buffer for nonlocal exits. */ +EXTERN jmp_buf diff_abort_buf; +#define DIFF_ABORT(retval) longjmp(diff_abort_buf, retval) + +/* The result of comparison is an "edit script": a chain of `struct change'. + Each `struct change' represents one place where some lines are deleted + and some are inserted. + + LINE0 and LINE1 are the first affected lines in the two files (origin 0). + DELETED is the number of lines deleted here from file 0. + INSERTED is the number of lines inserted here in file 1. + + If DELETED is 0 then LINE0 is the number of the line before + which the insertion was done; vice versa for INSERTED and LINE1. */ + +struct change +{ + struct change *link; /* Previous or next edit command */ + int inserted; /* # lines of file 1 changed here. */ + int deleted; /* # lines of file 0 changed here. */ + int line0; /* Line number of 1st deleted line. */ + int line1; /* Line number of 1st inserted line. */ + char ignore; /* Flag used in context.c */ +}; + +/* Structures that describe the input files. */ + +/* Data on one input file being compared. */ + +struct file_data { + int desc; /* File descriptor */ + char const *name; /* File name */ + struct stat stat; /* File status from fstat() */ + int dir_p; /* nonzero if file is a directory */ + + /* Buffer in which text of file is read. */ + char * buffer; + /* Allocated size of buffer. */ + size_t bufsize; + /* Number of valid characters now in the buffer. */ + size_t buffered_chars; + + /* Array of pointers to lines in the file. */ + char const **linbuf; + + /* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines. + linebuf[linbuf_base ... buffered_lines - 1] are possibly differing. + linebuf[linbuf_base ... valid_lines - 1] contain valid data. + linebuf[linbuf_base ... alloc_lines - 1] are allocated. */ + int linbuf_base, buffered_lines, valid_lines, alloc_lines; + + /* Pointer to end of prefix of this file to ignore when hashing. */ + char const *prefix_end; + + /* Count of lines in the prefix. + There are this many lines in the file before linbuf[0]. */ + int prefix_lines; + + /* Pointer to start of suffix of this file to ignore when hashing. */ + char const *suffix_begin; + + /* Vector, indexed by line number, containing an equivalence code for + each line. It is this vector that is actually compared with that + of another file to generate differences. */ + int *equivs; + + /* Vector, like the previous one except that + the elements for discarded lines have been squeezed out. */ + int *undiscarded; + + /* Vector mapping virtual line numbers (not counting discarded lines) + to real ones (counting those lines). Both are origin-0. */ + int *realindexes; + + /* Total number of nondiscarded lines. */ + int nondiscarded_lines; + + /* Vector, indexed by real origin-0 line number, + containing 1 for a line that is an insertion or a deletion. + The results of comparison are stored here. */ + char *changed_flag; + + /* 1 if file ends in a line with no final newline. */ + int missing_newline; + + /* 1 more than the maximum equivalence value used for this or its + sibling file. */ + int equiv_max; +}; + +/* Describe the two files currently being compared. */ + +EXTERN struct file_data files[2]; + +/* Stdio stream to output diffs to. */ + +EXTERN FILE *outfile; + +/* Declare various functions. */ + +/* analyze.c */ +int diff_2_files PARAMS((struct file_data[], int)); + +/* context.c */ +void print_context_header PARAMS((struct file_data[], int)); +void print_context_script PARAMS((struct change *, int)); + +/* diff.c */ +int excluded_filename PARAMS((char const *)); + +/* dir.c */ +int diff_dirs PARAMS((struct file_data const[], int (*) PARAMS((char const *, char const *, char const *, char const *, int)), int)); + +/* ed.c */ +void print_ed_script PARAMS((struct change *)); +void pr_forward_ed_script PARAMS((struct change *)); + +/* ifdef.c */ +void print_ifdef_script PARAMS((struct change *)); + +/* io.c */ +int read_files PARAMS((struct file_data[], int)); +int sip PARAMS((struct file_data *, int)); +void slurp PARAMS((struct file_data *)); + +/* normal.c */ +void print_normal_script PARAMS((struct change *)); + +/* rcs.c */ +void print_rcs_script PARAMS((struct change *)); + +/* side.c */ +void print_sdiff_script PARAMS((struct change *)); + +/* util.c */ +VOID *xmalloc PARAMS((size_t)); +VOID *xrealloc PARAMS((VOID *, size_t)); +char *concat PARAMS((char const *, char const *, char const *)); +char *dir_file_pathname PARAMS((char const *, char const *)); +int change_letter PARAMS((int, int)); +int line_cmp PARAMS((char const *, char const *)); +int translate_line_number PARAMS((struct file_data const *, int)); +struct change *find_change PARAMS((struct change *)); +struct change *find_reverse_change PARAMS((struct change *)); +void analyze_hunk PARAMS((struct change *, int *, int *, int *, int *, int *, int *)); +void begin_output PARAMS((void)); +void debug_script PARAMS((struct change *)); +void diff_error PARAMS((char const *, char const *, char const *)); +void fatal PARAMS((char const *)); +void finish_output PARAMS((void)); +void write_output PARAMS((char const *, size_t)); +void printf_output PARAMS((char const *, ...)) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 6) + __attribute__ ((__format__ (__printf__, 1, 2))) +#endif + ; +void flush_output PARAMS((void)); +void message PARAMS((char const *, char const *, char const *)); +void message5 PARAMS((char const *, char const *, char const *, char const *, char const *)); +void output_1_line PARAMS((char const *, char const *, char const *, char const *)); +void perror_with_name PARAMS((char const *)); +void pfatal_with_name PARAMS((char const *)); +void print_1_line PARAMS((char const *, char const * const *)); +void print_message_queue PARAMS((void)); +void print_number_range PARAMS((int, struct file_data *, int, int)); +void print_script PARAMS((struct change *, struct change * (*) PARAMS((struct change *)), void (*) PARAMS((struct change *)))); +void setup_output PARAMS((char const *, char const *, int)); +void translate_range PARAMS((struct file_data const *, int, int, int *, int *)); + +/* version.c */ +extern char const diff_version_string[]; diff --git a/contrib/cvs-1.12.9/diff/diff3.c b/contrib/cvs-1.12.9/diff/diff3.c new file mode 100644 index 0000000000..2511187f27 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/diff3.c @@ -0,0 +1,1927 @@ +/* Three way file comparison program (diff3) for Project GNU. + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + */ + +/* Written by Randy Smith */ +/* Librarification by Tim Pierce */ + +#include "system.h" +#include +#include +#include "getopt.h" +#include "diffrun.h" + +/* diff3.c has a real initialize_main function. */ +#ifdef initialize_main +#undef initialize_main +#endif + +extern char const diff_version_string[]; + +extern FILE *outfile; + +extern const struct diff_callbacks *callbacks; + +void write_output PARAMS((char const *, size_t)); +void printf_output PARAMS((char const *, ...)) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 6) + __attribute__ ((__format__ (__printf__, 1, 2))) +#endif + ; +void flush_output PARAMS((void)); + +char * cvs_temp_name PARAMS((void)); + +/* + * Internal data structures and macros for the diff3 program; includes + * data structures for both diff3 diffs and normal diffs. + */ + +/* Different files within a three way diff. */ +#define FILE0 0 +#define FILE1 1 +#define FILE2 2 + +/* + * A three way diff is built from two two-way diffs; the file which + * the two two-way diffs share is: + */ +#define FILEC FILE2 + +/* + * Different files within a two way diff. + * FC is the common file, FO the other file. + */ +#define FO 0 +#define FC 1 + +/* The ranges are indexed by */ +#define START 0 +#define END 1 + +enum diff_type { + ERROR, /* Should not be used */ + ADD, /* Two way diff add */ + CHANGE, /* Two way diff change */ + DELETE, /* Two way diff delete */ + DIFF_ALL, /* All three are different */ + DIFF_1ST, /* Only the first is different */ + DIFF_2ND, /* Only the second */ + DIFF_3RD /* Only the third */ +}; + +/* Two way diff */ +struct diff_block { + int ranges[2][2]; /* Ranges are inclusive */ + char **lines[2]; /* The actual lines (may contain nulls) */ + size_t *lengths[2]; /* Line lengths (including newlines, if any) */ + struct diff_block *next; +}; + +/* Three way diff */ + +struct diff3_block { + enum diff_type correspond; /* Type of diff */ + int ranges[3][2]; /* Ranges are inclusive */ + char **lines[3]; /* The actual lines (may contain nulls) */ + size_t *lengths[3]; /* Line lengths (including newlines, if any) */ + struct diff3_block *next; +}; + +/* + * Access the ranges on a diff block. + */ +#define D_LOWLINE(diff, filenum) \ + ((diff)->ranges[filenum][START]) +#define D_HIGHLINE(diff, filenum) \ + ((diff)->ranges[filenum][END]) +#define D_NUMLINES(diff, filenum) \ + (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1) + +/* + * Access the line numbers in a file in a diff by relative line + * numbers (i.e. line number within the diff itself). Note that these + * are lvalues and can be used for assignment. + */ +#define D_RELNUM(diff, filenum, linenum) \ + ((diff)->lines[filenum][linenum]) +#define D_RELLEN(diff, filenum, linenum) \ + ((diff)->lengths[filenum][linenum]) + +/* + * And get at them directly, when that should be necessary. + */ +#define D_LINEARRAY(diff, filenum) \ + ((diff)->lines[filenum]) +#define D_LENARRAY(diff, filenum) \ + ((diff)->lengths[filenum]) + +/* + * Next block. + */ +#define D_NEXT(diff) ((diff)->next) + +/* + * Access the type of a diff3 block. + */ +#define D3_TYPE(diff) ((diff)->correspond) + +/* + * Line mappings based on diffs. The first maps off the top of the + * diff, the second off of the bottom. + */ +#define D_HIGH_MAPLINE(diff, fromfile, tofile, lineno) \ + ((lineno) \ + - D_HIGHLINE ((diff), (fromfile)) \ + + D_HIGHLINE ((diff), (tofile))) + +#define D_LOW_MAPLINE(diff, fromfile, tofile, lineno) \ + ((lineno) \ + - D_LOWLINE ((diff), (fromfile)) \ + + D_LOWLINE ((diff), (tofile))) + +/* + * General memory allocation function. + */ +#define ALLOCATE(number, type) \ + (type *) xmalloc ((number) * sizeof (type)) + +/* Options variables for flags set on command line. */ + +/* If nonzero, treat all files as text files, never as binary. */ +static int always_text; + +/* If nonzero, write out an ed script instead of the standard diff3 format. */ +static int edscript; + +/* If nonzero, in the case of overlapping diffs (type DIFF_ALL), + preserve the lines which would normally be deleted from + file 1 with a special flagging mechanism. */ +static int flagging; + +/* Number of lines to keep in identical prefix and suffix. */ +static int const horizon_lines = 10; + +/* Use a tab to align output lines (-T). */ +static int tab_align_flag; + +/* If nonzero, do not output information for overlapping diffs. */ +static int simple_only; + +/* If nonzero, do not output information for non-overlapping diffs. */ +static int overlap_only; + +/* If nonzero, show information for DIFF_2ND diffs. */ +static int show_2nd; + +/* If nonzero, include `:wq' at the end of the script + to write out the file being edited. */ +static int finalwrite; + +/* If nonzero, output a merged file. */ +static int merge; + +extern char *diff_program_name; + +static char *read_diff PARAMS((char const *, char const *, char **)); +static char *scan_diff_line PARAMS((char *, char **, size_t *, char *, int)); +static enum diff_type process_diff_control PARAMS((char **, struct diff_block *)); +static int compare_line_list PARAMS((char * const[], size_t const[], char * const[], size_t const[], int)); +static int copy_stringlist PARAMS((char * const[], size_t const[], char *[], size_t[], int)); +static int dotlines PARAMS((struct diff3_block *, int)); +static int output_diff3_edscript PARAMS((struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *)); +static int output_diff3_merge PARAMS((FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *)); +static size_t myread PARAMS((int, char *, size_t)); +static struct diff3_block *create_diff3_block PARAMS((int, int, int, int, int, int)); +static struct diff3_block *make_3way_diff PARAMS((struct diff_block *, struct diff_block *)); +static struct diff3_block *reverse_diff3_blocklist PARAMS((struct diff3_block *)); +static struct diff3_block *using_to_diff3_block PARAMS((struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *)); +static struct diff_block *process_diff PARAMS((char const *, char const *, struct diff_block **, char **)); +static void check_output PARAMS((FILE *)); +static void diff3_fatal PARAMS((char const *)); +static void output_diff3 PARAMS((struct diff3_block *, int const[3], int const[3])); +static void diff3_perror_with_exit PARAMS((char const *)); +static int try_help PARAMS((char const *)); +static void undotlines PARAMS((int, int, int)); +static void usage PARAMS((void)); +static void initialize_main PARAMS((int *, char ***)); +static void free_diff_blocks PARAMS((struct diff_block *)); +static void free_diff3_blocks PARAMS((struct diff3_block *)); + +/* Functions provided in libdiff.a or other external sources. */ +VOID *xmalloc PARAMS((size_t)); +VOID *xrealloc PARAMS((VOID *, size_t)); +void perror_with_name PARAMS((char const *)); +void diff_error PARAMS((char const *, char const *, char const *)); + +/* Permit non-local exits from diff3. */ +static jmp_buf diff3_abort_buf; +#define DIFF3_ABORT(retval) longjmp(diff3_abort_buf, retval) + +static struct option const longopts[] = +{ + {"text", 0, 0, 'a'}, + {"show-all", 0, 0, 'A'}, + {"ed", 0, 0, 'e'}, + {"show-overlap", 0, 0, 'E'}, + {"label", 1, 0, 'L'}, + {"merge", 0, 0, 'm'}, + {"initial-tab", 0, 0, 'T'}, + {"overlap-only", 0, 0, 'x'}, + {"easy-only", 0, 0, '3'}, + {"version", 0, 0, 'v'}, + {"help", 0, 0, 129}, + {0, 0, 0, 0} +}; + +/* + * Main program. Calls diff twice on two pairs of input files, + * combines the two diffs, and outputs them. + */ +int +diff3_run (argc, argv, out, callbacks_arg) + int argc; + char **argv; + char *out; + const struct diff_callbacks *callbacks_arg; +{ + int c, i; + int mapping[3]; + int rev_mapping[3]; + int incompat = 0; + int conflicts_found; + int status; + struct diff_block *thread0, *thread1, *last_block; + char *content0, *content1; + struct diff3_block *diff3; + int tag_count = 0; + char *tag_strings[3]; + char *commonname; + char **file; + struct stat statb; + int optind_old; + int opened_file = 0; + + callbacks = callbacks_arg; + + initialize_main (&argc, &argv); + + optind_old = optind; + optind = 0; + while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != EOF) + { + switch (c) + { + case 'a': + always_text = 1; + break; + case 'A': + show_2nd = 1; + flagging = 1; + incompat++; + break; + case 'x': + overlap_only = 1; + incompat++; + break; + case '3': + simple_only = 1; + incompat++; + break; + case 'i': + finalwrite = 1; + break; + case 'm': + merge = 1; + break; + case 'X': + overlap_only = 1; + /* Falls through */ + case 'E': + flagging = 1; + /* Falls through */ + case 'e': + incompat++; + break; + case 'T': + tab_align_flag = 1; + break; + case 'v': + if (callbacks && callbacks->write_stdout) + { + (*callbacks->write_stdout) ("diff3 - GNU diffutils version "); + (*callbacks->write_stdout) (diff_version_string); + (*callbacks->write_stdout) ("\n"); + } + else + printf ("diff3 - GNU diffutils version %s\n", diff_version_string); + return 0; + case 129: + usage (); + if (! callbacks || ! callbacks->write_stdout) + check_output (stdout); + return 0; + case 'L': + /* Handle up to three -L options. */ + if (tag_count < 3) + { + tag_strings[tag_count++] = optarg; + break; + } + return try_help ("Too many labels were given. The limit is 3."); + default: + return try_help (0); + } + } + + edscript = incompat & ~merge; /* -AeExX3 without -m implies ed script. */ + show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */ + flagging |= ~incompat & merge; + + if (incompat > 1 /* Ensure at most one of -AeExX3. */ + || finalwrite & merge /* -i -m would rewrite input file. */ + || (tag_count && ! flagging)) /* -L requires one of -AEX. */ + return try_help ("incompatible options"); + + if (argc - optind != 3) + return try_help (argc - optind < 3 ? "missing operand" : "extra operand"); + + file = &argv[optind]; + + optind = optind_old; + + for (i = tag_count; i < 3; i++) + tag_strings[i] = file[i]; + + /* Always compare file1 to file2, even if file2 is "-". + This is needed for -mAeExX3. Using the file0 as + the common file would produce wrong results, because if the + file0-file1 diffs didn't line up with the file0-file2 diffs + (which is entirely possible since we don't use diff's -n option), + diff3 might report phantom changes from file1 to file2. */ + /* Also try to compare file0 to file1 because this is the where + changes are expected to come from. Diffing between these pairs + of files is is most likely to return the intended changes. There + can also be the same problem with phantom changes from file0 to + file1. */ + /* Historically, the default common file was file2. Ediff for emacs + and possibly other applications, have therefore made file2 the + ancestor. So, for compatibility, if this is simply a three + way diff (not a merge or edscript) then use the old way with + file2 as the common file. */ + + { + int common; + if (edscript || merge ) + { + common = 1; + } + else + { + common = 2; + } + if (strcmp (file[common], "-") == 0) + { + /* Sigh. We've got standard input as the arg corresponding to + the desired common file. We can't call diff twice on + stdin. Use another arg as the common file instead. */ + common = 3 - common; + if (strcmp (file[0], "-") == 0 || strcmp (file[common], "-") == 0) + { + diff_error ("%s", "`-' specified for more than one input file", 0); + return 2; + } + } + + mapping[0] = 0; + mapping[1] = 3 - common; + mapping[2] = common; + } + + for (i = 0; i < 3; i++) + rev_mapping[mapping[i]] = i; + + for (i = 0; i < 3; i++) + if (strcmp (file[i], "-") != 0) + { + if (stat (file[i], &statb) < 0) + { + perror_with_name (file[i]); + return 2; + } + else if (S_ISDIR(statb.st_mode)) + { + diff_error ("%s: Is a directory", file[i], 0); + return 2; + } + } + + if (callbacks && callbacks->write_output) + { + if (out != NULL) + { + diff_error ("write callback with output file", 0, 0); + return 2; + } + } + else + { + if (out == NULL) + outfile = stdout; + else + { + outfile = fopen (out, "w"); + if (outfile == NULL) + { + perror_with_name (out); + return 2; + } + opened_file = 1; + } + } + + /* Set the jump buffer, so that diff may abort execution without + terminating the process. */ + status = setjmp (diff3_abort_buf); + if (status != 0) + return status; + + commonname = file[rev_mapping[FILEC]]; + thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block, + &content1); + /* What is the intention behind determining horizon_lines from first + diff? I think it is better to use the same parameters for each + diff so that equal differences in each diff will appear the + same. */ + /* + if (thread1) + for (i = 0; i < 2; i++) + { + horizon_lines = max (horizon_lines, D_NUMLINES (thread1, i)); + horizon_lines = max (horizon_lines, D_NUMLINES (last_block, i)); + } + */ + thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block, + &content0); + diff3 = make_3way_diff (thread0, thread1); + if (edscript) + conflicts_found + = output_diff3_edscript (diff3, mapping, rev_mapping, + tag_strings[0], tag_strings[1], tag_strings[2]); + else if (merge) + { + FILE *mfp = fopen (file[rev_mapping[FILE0]], "r"); + if (! mfp) + diff3_perror_with_exit (file[rev_mapping[FILE0]]); + conflicts_found = output_diff3_merge (mfp, diff3, mapping, rev_mapping, + tag_strings[0], tag_strings[1], tag_strings[2]); + if (ferror (mfp)) + diff3_fatal ("read error"); + if (fclose(mfp) != 0) + perror_with_name (file[rev_mapping[FILE0]]); + } + else + { + output_diff3 (diff3, mapping, rev_mapping); + conflicts_found = 0; + } + + free(content0); + free(content1); + free_diff3_blocks(diff3); + + if (! callbacks || ! callbacks->write_output) + check_output (outfile); + + if (opened_file) + if (fclose (outfile) != 0) + perror_with_name ("close error on output file"); + + return conflicts_found; +} + +static int +try_help (reason) + char const *reason; +{ + if (reason) + diff_error ("%s", reason, 0); + diff_error ("Try `%s --help' for more information.", diff_program_name, 0); + return 2; +} + +static void +check_output (stream) + FILE *stream; +{ + if (ferror (stream) || fflush (stream) != 0) + diff3_fatal ("write error"); +} + +/* + * Explain, patiently and kindly, how to use this program. + */ +static void +usage () +{ + if (callbacks && callbacks->write_stdout) + { + (*callbacks->write_stdout) ("Usage: "); + (*callbacks->write_stdout) (diff_program_name); + (*callbacks->write_stdout) (" [OPTION]... MYFILE OLDFILE YOURFILE\n\n"); + + (*callbacks->write_stdout) ("\ + -e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE.\n\ + -E --show-overlap Output unmerged changes, bracketing conflicts.\n\ + -A --show-all Output all changes, bracketing conflicts.\n\ + -x --overlap-only Output overlapping changes.\n\ + -X Output overlapping changes, bracketing them.\n\ + -3 --easy-only Output unmerged nonoverlapping changes.\n\n"); + (*callbacks->write_stdout) ("\ + -m --merge Output merged file instead of ed script (default -A).\n\ + -L LABEL --label=LABEL Use LABEL instead of file name.\n\ + -i Append `w' and `q' commands to ed scripts.\n\ + -a --text Treat all files as text.\n\ + -T --initial-tab Make tabs line up by prepending a tab.\n\n"); + (*callbacks->write_stdout) ("\ + -v --version Output version info.\n\ + --help Output this help.\n\n"); + (*callbacks->write_stdout) ("If a FILE is `-', read standard input.\n"); + } + else + { + printf ("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n\n", diff_program_name); + + printf ("%s", "\ + -e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE.\n\ + -E --show-overlap Output unmerged changes, bracketing conflicts.\n\ + -A --show-all Output all changes, bracketing conflicts.\n\ + -x --overlap-only Output overlapping changes.\n\ + -X Output overlapping changes, bracketing them.\n\ + -3 --easy-only Output unmerged nonoverlapping changes.\n\n"); + printf ("%s", "\ + -m --merge Output merged file instead of ed script (default -A).\n\ + -L LABEL --label=LABEL Use LABEL instead of file name.\n\ + -i Append `w' and `q' commands to ed scripts.\n\ + -a --text Treat all files as text.\n\ + -T --initial-tab Make tabs line up by prepending a tab.\n\n"); + printf ("%s", "\ + -v --version Output version info.\n\ + --help Output this help.\n\n"); + printf ("If a FILE is `-', read standard input.\n"); + } +} + +/* + * Routines that combine the two diffs together into one. The + * algorithm used follows: + * + * File2 is shared in common between the two diffs. + * Diff02 is the diff between 0 and 2. + * Diff12 is the diff between 1 and 2. + * + * 1) Find the range for the first block in File2. + * a) Take the lowest of the two ranges (in File2) in the two + * current blocks (one from each diff) as being the low + * water mark. Assign the upper end of this block as + * being the high water mark and move the current block up + * one. Mark the block just moved over as to be used. + * b) Check the next block in the diff that the high water + * mark is *not* from. + * + * *If* the high water mark is above + * the low end of the range in that block, + * + * mark that block as to be used and move the current + * block up. Set the high water mark to the max of + * the high end of this block and the current. Repeat b. + * + * 2) Find the corresponding ranges in File0 (from the blocks + * in diff02; line per line outside of diffs) and in File1. + * Create a diff3_block, reserving space as indicated by the ranges. + * + * 3) Copy all of the pointers for file2 in. At least for now, + * do memcmp's between corresponding strings in the two diffs. + * + * 4) Copy all of the pointers for file0 and 1 in. Get what you + * need from file2 (when there isn't a diff block, it's + * identical to file2 within the range between diff blocks). + * + * 5) If the diff blocks you used came from only one of the two + * strings of diffs, then that file (i.e. the one other than + * the common file in that diff) is the odd person out. If you used + * diff blocks from both sets, check to see if files 0 and 1 match: + * + * Same number of lines? If so, do a set of memcmp's (if a + * memcmp matches; copy the pointer over; it'll be easier later + * if you have to do any compares). If they match, 0 & 1 are + * the same. If not, all three different. + * + * Then you do it again, until you run out of blocks. + * + */ + +/* + * This routine makes a three way diff (chain of diff3_block's) from two + * two way diffs (chains of diff_block's). It is assumed that each of + * the two diffs passed are onto the same file (i.e. that each of the + * diffs were made "to" the same file). The three way diff pointer + * returned will have numbering FILE0--the other file in diff02, + * FILE1--the other file in diff12, and FILEC--the common file. + */ +static struct diff3_block * +make_3way_diff (thread0, thread1) + struct diff_block *thread0, *thread1; +{ +/* + * This routine works on the two diffs passed to it as threads. + * Thread number 0 is diff02, thread number 1 is diff12. The USING + * array is set to the base of the list of blocks to be used to + * construct each block of the three way diff; if no blocks from a + * particular thread are to be used, that element of the using array + * is set to 0. The elements LAST_USING array are set to the last + * elements on each of the using lists. + * + * The HIGH_WATER_MARK is set to the highest line number in the common file + * described in any of the diffs in either of the USING lists. The + * HIGH_WATER_THREAD names the thread. Similarly the BASE_WATER_MARK + * and BASE_WATER_THREAD describe the lowest line number in the common file + * described in any of the diffs in either of the USING lists. The + * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was + * taken. + * + * The HIGH_WATER_DIFF should always be equal to LAST_USING + * [HIGH_WATER_THREAD]. The OTHER_DIFF is the next diff to check for + * higher water, and should always be equal to + * CURRENT[HIGH_WATER_THREAD ^ 0x1]. The OTHER_THREAD is the thread + * in which the OTHER_DIFF is, and hence should always be equal to + * HIGH_WATER_THREAD ^ 0x1. + * + * The variable LAST_DIFF is kept set to the last diff block produced + * by this routine, for line correspondence purposes between that diff + * and the one currently being worked on. It is initialized to + * ZERO_DIFF before any blocks have been created. + */ + + struct diff_block + *using[2], + *last_using[2], + *current[2]; + + int + high_water_mark; + + int + high_water_thread, + base_water_thread, + other_thread; + + struct diff_block + *high_water_diff, + *other_diff; + + struct diff3_block + *result, + *tmpblock, + **result_end; + + struct diff3_block const *last_diff3; + + static struct diff3_block const zero_diff3 = { 0 }; + + /* Initialization */ + result = 0; + result_end = &result; + current[0] = thread0; current[1] = thread1; + last_diff3 = &zero_diff3; + + /* Sniff up the threads until we reach the end */ + + while (current[0] || current[1]) + { + using[0] = using[1] = last_using[0] = last_using[1] = 0; + + /* Setup low and high water threads, diffs, and marks. */ + if (!current[0]) + base_water_thread = 1; + else if (!current[1]) + base_water_thread = 0; + else + base_water_thread = + (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC)); + + high_water_thread = base_water_thread; + + high_water_diff = current[high_water_thread]; + +#if 0 + /* low and high waters start off same diff */ + base_water_mark = D_LOWLINE (high_water_diff, FC); +#endif + + high_water_mark = D_HIGHLINE (high_water_diff, FC); + + /* Make the diff you just got info from into the using class */ + using[high_water_thread] + = last_using[high_water_thread] + = high_water_diff; + current[high_water_thread] = high_water_diff->next; + last_using[high_water_thread]->next = 0; + + /* And mark the other diff */ + other_thread = high_water_thread ^ 0x1; + other_diff = current[other_thread]; + + /* Shuffle up the ladder, checking the other diff to see if it + needs to be incorporated. */ + while (other_diff + && D_LOWLINE (other_diff, FC) <= high_water_mark + 1) + { + + /* Incorporate this diff into the using list. Note that + this doesn't take it off the current list */ + if (using[other_thread]) + last_using[other_thread]->next = other_diff; + else + using[other_thread] = other_diff; + last_using[other_thread] = other_diff; + + /* Take it off the current list. Note that this following + code assumes that other_diff enters it equal to + current[high_water_thread ^ 0x1] */ + current[other_thread] = current[other_thread]->next; + other_diff->next = 0; + + /* Set the high_water stuff + If this comparison is equal, then this is the last pass + through this loop; since diff blocks within a given + thread cannot overlap, the high_water_mark will be + *below* the range_start of either of the next diffs. */ + + if (high_water_mark < D_HIGHLINE (other_diff, FC)) + { + high_water_thread ^= 1; + high_water_diff = other_diff; + high_water_mark = D_HIGHLINE (other_diff, FC); + } + + /* Set the other diff */ + other_thread = high_water_thread ^ 0x1; + other_diff = current[other_thread]; + } + + /* The using lists contain a list of all of the blocks to be + included in this diff3_block. Create it. */ + + tmpblock = using_to_diff3_block (using, last_using, + base_water_thread, high_water_thread, + last_diff3); + free_diff_blocks(using[0]); + free_diff_blocks(using[1]); + + if (!tmpblock) + diff3_fatal ("internal error: screwup in format of diff blocks"); + + /* Put it on the list. */ + *result_end = tmpblock; + result_end = &tmpblock->next; + + /* Set up corresponding lines correctly. */ + last_diff3 = tmpblock; + } + return result; +} + +/* + * using_to_diff3_block: + * This routine takes two lists of blocks (from two separate diff + * threads) and puts them together into one diff3 block. + * It then returns a pointer to this diff3 block or 0 for failure. + * + * All arguments besides using are for the convenience of the routine; + * they could be derived from the using array. + * LAST_USING is a pair of pointers to the last blocks in the using + * structure. + * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest + * and highest line numbers for File0. + * last_diff3 contains the last diff produced in the calling routine. + * This is used for lines mappings which would still be identical to + * the state that diff ended in. + * + * A distinction should be made in this routine between the two diffs + * that are part of a normal two diff block, and the three diffs that + * are part of a diff3_block. + */ +static struct diff3_block * +using_to_diff3_block (using, last_using, low_thread, high_thread, last_diff3) + struct diff_block + *using[2], + *last_using[2]; + int low_thread, high_thread; + struct diff3_block const *last_diff3; +{ + int low[2], high[2]; + struct diff3_block *result; + struct diff_block *ptr; + int d, i; + + /* Find the range in the common file. */ + int lowc = D_LOWLINE (using[low_thread], FC); + int highc = D_HIGHLINE (last_using[high_thread], FC); + + /* Find the ranges in the other files. + If using[d] is null, that means that the file to which that diff + refers is equivalent to the common file over this range. */ + + for (d = 0; d < 2; d++) + if (using[d]) + { + low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc); + high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc); + } + else + { + low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc); + high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc); + } + + /* Create a block with the appropriate sizes */ + result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc); + + /* Copy information for the common file. + Return with a zero if any of the compares failed. */ + + for (d = 0; d < 2; d++) + for (ptr = using[d]; ptr; ptr = D_NEXT (ptr)) + { + int result_offset = D_LOWLINE (ptr, FC) - lowc; + + if (!copy_stringlist (D_LINEARRAY (ptr, FC), + D_LENARRAY (ptr, FC), + D_LINEARRAY (result, FILEC) + result_offset, + D_LENARRAY (result, FILEC) + result_offset, + D_NUMLINES (ptr, FC))) + return 0; + } + + /* Copy information for file d. First deal with anything that might be + before the first diff. */ + + for (d = 0; d < 2; d++) + { + struct diff_block *u = using[d]; + int lo = low[d], hi = high[d]; + + for (i = 0; + i + lo < (u ? D_LOWLINE (u, FO) : hi + 1); + i++) + { + D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i); + D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i); + } + + for (ptr = u; ptr; ptr = D_NEXT (ptr)) + { + int result_offset = D_LOWLINE (ptr, FO) - lo; + int linec; + + if (!copy_stringlist (D_LINEARRAY (ptr, FO), + D_LENARRAY (ptr, FO), + D_LINEARRAY (result, FILE0 + d) + result_offset, + D_LENARRAY (result, FILE0 + d) + result_offset, + D_NUMLINES (ptr, FO))) + return 0; + + /* Catch the lines between here and the next diff */ + linec = D_HIGHLINE (ptr, FC) + 1 - lowc; + for (i = D_HIGHLINE (ptr, FO) + 1 - lo; + i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo; + i++) + { + D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec); + D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec); + linec++; + } + } + } + + /* Set correspond */ + if (!using[0]) + D3_TYPE (result) = DIFF_2ND; + else if (!using[1]) + D3_TYPE (result) = DIFF_1ST; + else + { + int nl0 = D_NUMLINES (result, FILE0); + int nl1 = D_NUMLINES (result, FILE1); + + if (nl0 != nl1 + || !compare_line_list (D_LINEARRAY (result, FILE0), + D_LENARRAY (result, FILE0), + D_LINEARRAY (result, FILE1), + D_LENARRAY (result, FILE1), + nl0)) + D3_TYPE (result) = DIFF_ALL; + else + D3_TYPE (result) = DIFF_3RD; + } + + return result; +} + +/* + * This routine copies pointers from a list of strings to a different list + * of strings. If a spot in the second list is already filled, it + * makes sure that it is filled with the same string; if not it + * returns 0, the copy incomplete. + * Upon successful completion of the copy, it returns 1. + */ +static int +copy_stringlist (fromptrs, fromlengths, toptrs, tolengths, copynum) + char * const fromptrs[]; + char *toptrs[]; + size_t const fromlengths[]; + size_t tolengths[]; + int copynum; +{ + register char * const *f = fromptrs; + register char **t = toptrs; + register size_t const *fl = fromlengths; + register size_t *tl = tolengths; + + while (copynum--) + { + if (*t) + { if (*fl != *tl || memcmp (*f, *t, *fl)) return 0; } + else + { *t = *f ; *tl = *fl; } + + t++; f++; tl++; fl++; + } + return 1; +} + +/* + * Create a diff3_block, with ranges as specified in the arguments. + * Allocate the arrays for the various pointers (and zero them) based + * on the arguments passed. Return the block as a result. + */ +static struct diff3_block * +create_diff3_block (low0, high0, low1, high1, low2, high2) + register int low0, high0, low1, high1, low2, high2; +{ + struct diff3_block *result = ALLOCATE (1, struct diff3_block); + int numlines; + + D3_TYPE (result) = ERROR; + D_NEXT (result) = 0; + + /* Assign ranges */ + D_LOWLINE (result, FILE0) = low0; + D_HIGHLINE (result, FILE0) = high0; + D_LOWLINE (result, FILE1) = low1; + D_HIGHLINE (result, FILE1) = high1; + D_LOWLINE (result, FILE2) = low2; + D_HIGHLINE (result, FILE2) = high2; + + /* Allocate and zero space */ + numlines = D_NUMLINES (result, FILE0); + if (numlines) + { + D_LINEARRAY (result, FILE0) = ALLOCATE (numlines, char *); + D_LENARRAY (result, FILE0) = ALLOCATE (numlines, size_t); + bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *))); + bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (size_t))); + } + else + { + D_LINEARRAY (result, FILE0) = 0; + D_LENARRAY (result, FILE0) = 0; + } + + numlines = D_NUMLINES (result, FILE1); + if (numlines) + { + D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *); + D_LENARRAY (result, FILE1) = ALLOCATE (numlines, size_t); + bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *))); + bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (size_t))); + } + else + { + D_LINEARRAY (result, FILE1) = 0; + D_LENARRAY (result, FILE1) = 0; + } + + numlines = D_NUMLINES (result, FILE2); + if (numlines) + { + D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *); + D_LENARRAY (result, FILE2) = ALLOCATE (numlines, size_t); + bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *))); + bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (size_t))); + } + else + { + D_LINEARRAY (result, FILE2) = 0; + D_LENARRAY (result, FILE2) = 0; + } + + /* Return */ + return result; +} + +/* + * Compare two lists of lines of text. + * Return 1 if they are equivalent, 0 if not. + */ +static int +compare_line_list (list1, lengths1, list2, lengths2, nl) + char * const list1[], * const list2[]; + size_t const lengths1[], lengths2[]; + int nl; +{ + char + * const *l1 = list1, + * const *l2 = list2; + size_t const + *lgths1 = lengths1, + *lgths2 = lengths2; + + while (nl--) + if (!*l1 || !*l2 || *lgths1 != *lgths2++ + || memcmp (*l1++, *l2++, *lgths1++)) + return 0; + return 1; +} + +/* + * Routines to input and parse two way diffs. + */ + +extern char **environ; + +static struct diff_block * +process_diff (filea, fileb, last_block, diff_contents) + char const *filea, *fileb; + struct diff_block **last_block; + char **diff_contents; +{ + char *diff_limit; + char *scan_diff; + enum diff_type dt; + int i; + struct diff_block *block_list, **block_list_end, *bptr; + + diff_limit = read_diff (filea, fileb, diff_contents); + scan_diff = *diff_contents; + block_list_end = &block_list; + bptr = 0; /* Pacify `gcc -W'. */ + + while (scan_diff < diff_limit) + { + bptr = ALLOCATE (1, struct diff_block); + bptr->lines[0] = bptr->lines[1] = 0; + bptr->lengths[0] = bptr->lengths[1] = 0; + + dt = process_diff_control (&scan_diff, bptr); + if (dt == ERROR || *scan_diff != '\n') + { + char *serr; + + for (serr = scan_diff; *serr != '\n'; serr++) + ; + *serr = '\0'; + diff_error ("diff error: %s", scan_diff, 0); + *serr = '\n'; + DIFF3_ABORT (2); + } + scan_diff++; + + /* Force appropriate ranges to be null, if necessary */ + switch (dt) + { + case ADD: + bptr->ranges[0][0]++; + break; + case DELETE: + bptr->ranges[1][0]++; + break; + case CHANGE: + break; + default: + diff3_fatal ("internal error: invalid diff type in process_diff"); + break; + } + + /* Allocate space for the pointers for the lines from filea, and + parcel them out among these pointers */ + if (dt != ADD) + { + int numlines = D_NUMLINES (bptr, 0); + bptr->lines[0] = ALLOCATE (numlines, char *); + bptr->lengths[0] = ALLOCATE (numlines, size_t); + for (i = 0; i < numlines; i++) + scan_diff = scan_diff_line (scan_diff, + &(bptr->lines[0][i]), + &(bptr->lengths[0][i]), + diff_limit, + '<'); + } + + /* Get past the separator for changes */ + if (dt == CHANGE) + { + if (strncmp (scan_diff, "---\n", 4)) + diff3_fatal ("invalid diff format; invalid change separator"); + scan_diff += 4; + } + + /* Allocate space for the pointers for the lines from fileb, and + parcel them out among these pointers */ + if (dt != DELETE) + { + int numlines = D_NUMLINES (bptr, 1); + bptr->lines[1] = ALLOCATE (numlines, char *); + bptr->lengths[1] = ALLOCATE (numlines, size_t); + for (i = 0; i < numlines; i++) + scan_diff = scan_diff_line (scan_diff, + &(bptr->lines[1][i]), + &(bptr->lengths[1][i]), + diff_limit, + '>'); + } + + /* Place this block on the blocklist. */ + *block_list_end = bptr; + block_list_end = &bptr->next; + } + + *block_list_end = 0; + *last_block = bptr; + return block_list; +} + +/* + * This routine will parse a normal format diff control string. It + * returns the type of the diff (ERROR if the format is bad). All of + * the other important information is filled into to the structure + * pointed to by db, and the string pointer (whose location is passed + * to this routine) is updated to point beyond the end of the string + * parsed. Note that only the ranges in the diff_block will be set by + * this routine. + * + * If some specific pair of numbers has been reduced to a single + * number, then both corresponding numbers in the diff block are set + * to that number. In general these numbers are interpetted as ranges + * inclusive, unless being used by the ADD or DELETE commands. It is + * assumed that these will be special cased in a superior routine. + */ + +static enum diff_type +process_diff_control (string, db) + char **string; + struct diff_block *db; +{ + char *s = *string; + int holdnum; + enum diff_type type; + +/* These macros are defined here because they can use variables + defined in this function. Don't try this at home kids, we're + trained professionals! + + Also note that SKIPWHITE only recognizes tabs and spaces, and + that READNUM can only read positive, integral numbers */ + +#define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; } +#define READNUM(s, num) \ + { unsigned char c = *s; if (!ISDIGIT (c)) return ERROR; holdnum = 0; \ + do { holdnum = (c - '0' + holdnum * 10); } \ + while (ISDIGIT (c = *++s)); (num) = holdnum; } + + /* Read first set of digits */ + SKIPWHITE (s); + READNUM (s, db->ranges[0][START]); + + /* Was that the only digit? */ + SKIPWHITE (s); + if (*s == ',') + { + /* Get the next digit */ + s++; + READNUM (s, db->ranges[0][END]); + } + else + db->ranges[0][END] = db->ranges[0][START]; + + /* Get the letter */ + SKIPWHITE (s); + switch (*s) + { + case 'a': + type = ADD; + break; + case 'c': + type = CHANGE; + break; + case 'd': + type = DELETE; + break; + default: + return ERROR; /* Bad format */ + } + s++; /* Past letter */ + + /* Read second set of digits */ + SKIPWHITE (s); + READNUM (s, db->ranges[1][START]); + + /* Was that the only digit? */ + SKIPWHITE (s); + if (*s == ',') + { + /* Get the next digit */ + s++; + READNUM (s, db->ranges[1][END]); + SKIPWHITE (s); /* To move to end */ + } + else + db->ranges[1][END] = db->ranges[1][START]; + + *string = s; + return type; +} + +static char * +read_diff (filea, fileb, output_placement) + char const *filea, *fileb; + char **output_placement; +{ + char *diff_result; + size_t bytes, current_chunk_size, total; + int fd, wstatus; + struct stat pipestat; + FILE *outfile_hold; + const struct diff_callbacks *callbacks_hold; + struct diff_callbacks my_callbacks; + struct diff_callbacks *my_callbacks_arg; + + /* 302 / 1000 is log10(2.0) rounded up. Subtract 1 for the sign bit; + add 1 for integer division truncation; add 1 more for a minus sign. */ +#define INT_STRLEN_BOUND(type) ((sizeof(type)*CHAR_BIT - 1) * 302 / 1000 + 2) + + char const *argv[7]; + char horizon_arg[17 + INT_STRLEN_BOUND (int)]; + char const **ap; + char *diffout; + + ap = argv; + *ap++ = "diff"; + if (always_text) + *ap++ = "-a"; + sprintf (horizon_arg, "--horizon-lines=%d", horizon_lines); + *ap++ = horizon_arg; + *ap++ = "--"; + *ap++ = filea; + *ap++ = fileb; + *ap = 0; + + diffout = cvs_temp_name (); + + outfile_hold = outfile; + callbacks_hold = callbacks; + + /* We want to call diff_run preserving any stdout and stderr + callbacks, but discarding any callbacks to handle file output, + since we want the file output to go to our temporary file. + FIXME: We should use callbacks to just read it into a memory + buffer; that's we do with the temporary file just below anyhow. */ + if (callbacks == NULL) + my_callbacks_arg = NULL; + else + { + my_callbacks = *callbacks; + my_callbacks.write_output = NULL; + my_callbacks.flush_output = NULL; + my_callbacks_arg = &my_callbacks; + } + + wstatus = diff_run (ap - argv, (char **) argv, diffout, my_callbacks_arg); + + outfile = outfile_hold; + callbacks = callbacks_hold; + + if (wstatus == 2) + diff3_fatal ("subsidiary diff failed"); + + if (-1 == (fd = open (diffout, O_RDONLY))) + diff3_fatal ("could not open temporary diff file"); + + current_chunk_size = 8 * 1024; + if (fstat (fd, &pipestat) == 0) + current_chunk_size = max (current_chunk_size, STAT_BLOCKSIZE (pipestat)); + + diff_result = xmalloc (current_chunk_size); + total = 0; + do { + bytes = myread (fd, + diff_result + total, + current_chunk_size - total); + total += bytes; + if (total == current_chunk_size) + { + if (current_chunk_size < 2 * current_chunk_size) + current_chunk_size = 2 * current_chunk_size; + else if (current_chunk_size < (size_t) -1) + current_chunk_size = (size_t) -1; + else + diff3_fatal ("files are too large to fit into memory"); + diff_result = xrealloc (diff_result, (current_chunk_size *= 2)); + } + } while (bytes); + + if (total != 0 && diff_result[total-1] != '\n') + diff3_fatal ("invalid diff format; incomplete last line"); + + *output_placement = diff_result; + + if (close (fd) != 0) + diff3_perror_with_exit ("pipe close"); + unlink (diffout); + free( diffout ); + + return diff_result + total; +} + + +/* + * Scan a regular diff line (consisting of > or <, followed by a + * space, followed by text (including nulls) up to a newline. + * + * This next routine began life as a macro and many parameters in it + * are used as call-by-reference values. + */ +static char * +scan_diff_line (scan_ptr, set_start, set_length, limit, leadingchar) + char *scan_ptr, **set_start; + size_t *set_length; + char *limit; + int leadingchar; +{ + char *line_ptr; + + if (!(scan_ptr[0] == leadingchar + && scan_ptr[1] == ' ')) + diff3_fatal ("invalid diff format; incorrect leading line chars"); + + *set_start = line_ptr = scan_ptr + 2; + while (*line_ptr++ != '\n') + ; + + /* Include newline if the original line ended in a newline, + or if an edit script is being generated. + Copy any missing newline message to stderr if an edit script is being + generated, because edit scripts cannot handle missing newlines. + Return the beginning of the next line. */ + *set_length = line_ptr - *set_start; + if (line_ptr < limit && *line_ptr == '\\') + { + if (! edscript) + { + --*set_length; + line_ptr++; + while (*line_ptr++ != '\n') + ; + } + else + { + char *serr; + + line_ptr++; + serr = line_ptr; + while (*line_ptr++ != '\n') + ; + line_ptr[-1] = '\0'; + diff_error ("%s", serr, 0); + line_ptr[-1] = '\n'; + } + } + + return line_ptr; +} + +/* + * This routine outputs a three way diff passed as a list of + * diff3_block's. + * The argument MAPPING is indexed by external file number (in the + * argument list) and contains the internal file number (from the + * diff passed). This is important because the user expects his + * outputs in terms of the argument list number, and the diff passed + * may have been done slightly differently (if the last argument + * was "-", for example). + * REV_MAPPING is the inverse of MAPPING. + */ +static void +output_diff3 (diff, mapping, rev_mapping) + struct diff3_block *diff; + int const mapping[3], rev_mapping[3]; +{ + int i; + int oddoneout; + char *cp; + struct diff3_block *ptr; + int line; + size_t length; + int dontprint; + static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */ + char const *line_prefix = tab_align_flag ? "\t" : " "; + + for (ptr = diff; ptr; ptr = D_NEXT (ptr)) + { + char x[2]; + + switch (ptr->correspond) + { + case DIFF_ALL: + x[0] = '\0'; + dontprint = 3; /* Print them all */ + oddoneout = 3; /* Nobody's odder than anyone else */ + break; + case DIFF_1ST: + case DIFF_2ND: + case DIFF_3RD: + oddoneout = rev_mapping[(int) ptr->correspond - (int) DIFF_1ST]; + + x[0] = oddoneout + '1'; + x[1] = '\0'; + dontprint = oddoneout==0; + break; + default: + diff3_fatal ("internal error: invalid diff type passed to output"); + } + printf_output ("====%s\n", x); + + /* Go 0, 2, 1 if the first and third outputs are equivalent. */ + for (i = 0; i < 3; + i = (oddoneout == 1 ? skew_increment[i] : i + 1)) + { + int realfile = mapping[i]; + int + lowt = D_LOWLINE (ptr, realfile), + hight = D_HIGHLINE (ptr, realfile); + + printf_output ("%d:", i + 1); + switch (lowt - hight) + { + case 1: + printf_output ("%da\n", lowt - 1); + break; + case 0: + printf_output ("%dc\n", lowt); + break; + default: + printf_output ("%d,%dc\n", lowt, hight); + break; + } + + if (i == dontprint) continue; + + if (lowt <= hight) + { + line = 0; + do + { + printf_output (line_prefix); + cp = D_RELNUM (ptr, realfile, line); + length = D_RELLEN (ptr, realfile, line); + write_output (cp, length); + } + while (++line < hight - lowt + 1); + if (cp[length - 1] != '\n') + printf_output ("\n\\ No newline at end of file\n"); + } + } + } +} + + +/* + * Output the lines of B taken from FILENUM. + * Double any initial '.'s; yield nonzero if any initial '.'s were doubled. + */ +static int +dotlines (b, filenum) + struct diff3_block *b; + int filenum; +{ + int i; + int leading_dot = 0; + + for (i = 0; + i < D_NUMLINES (b, filenum); + i++) + { + char *line = D_RELNUM (b, filenum, i); + if (line[0] == '.') + { + leading_dot = 1; + write_output (".", 1); + } + write_output (line, D_RELLEN (b, filenum, i)); + } + + return leading_dot; +} + +/* + * Output to OUTPUTFILE a '.' line. If LEADING_DOT is nonzero, + * also output a command that removes initial '.'s + * starting with line START and continuing for NUM lines. + */ +static void +undotlines (leading_dot, start, num) + int leading_dot, start, num; +{ + write_output (".\n", 2); + if (leading_dot) + if (num == 1) + printf_output ("%ds/^\\.//\n", start); + else + printf_output ("%d,%ds/^\\.//\n", start, start + num - 1); +} + +/* + * This routine outputs a diff3 set of blocks as an ed script. This + * script applies the changes between file's 2 & 3 to file 1. It + * takes the precise format of the ed script to be output from global + * variables set during options processing. Note that it does + * destructive things to the set of diff3 blocks it is passed; it + * reverses their order (this gets around the problems involved with + * changing line numbers in an ed script). + * + * Note that this routine has the same problem of mapping as the last + * one did; the variable MAPPING maps from file number according to + * the argument list to file number according to the diff passed. All + * files listed below are in terms of the argument list. + * REV_MAPPING is the inverse of MAPPING. + * + * The arguments FILE0, FILE1 and FILE2 are the strings to print + * as the names of the three files. These may be the actual names, + * or may be the arguments specified with -L. + * + * Returns 1 if conflicts were found. + */ + +static int +output_diff3_edscript (diff, mapping, rev_mapping, file0, file1, file2) + struct diff3_block *diff; + int const mapping[3], rev_mapping[3]; + char const *file0, *file1, *file2; +{ + int leading_dot; + int conflicts_found = 0, conflict; + struct diff3_block *b; + + for (b = reverse_diff3_blocklist (diff); b; b = b->next) + { + /* Must do mapping correctly. */ + enum diff_type type + = ((b->correspond == DIFF_ALL) ? + DIFF_ALL : + ((enum diff_type) + (((int) DIFF_1ST) + + rev_mapping[(int) b->correspond - (int) DIFF_1ST]))); + + /* If we aren't supposed to do this output block, skip it. */ + switch (type) + { + default: continue; + case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break; + case DIFF_3RD: if (overlap_only) continue; conflict = 0; break; + case DIFF_ALL: if (simple_only) continue; conflict = flagging; break; + } + + if (conflict) + { + conflicts_found = 1; + + + /* Mark end of conflict. */ + + printf_output ("%da\n", D_HIGHLINE (b, mapping[FILE0])); + leading_dot = 0; + if (type == DIFF_ALL) + { + if (show_2nd) + { + /* Append lines from FILE1. */ + printf_output ("||||||| %s\n", file1); + leading_dot = dotlines (b, mapping[FILE1]); + } + /* Append lines from FILE2. */ + printf_output ("=======\n"); + leading_dot |= dotlines (b, mapping[FILE2]); + } + printf_output (">>>>>>> %s\n", file2); + undotlines (leading_dot, + D_HIGHLINE (b, mapping[FILE0]) + 2, + (D_NUMLINES (b, mapping[FILE1]) + + D_NUMLINES (b, mapping[FILE2]) + 1)); + + + /* Mark start of conflict. */ + + printf_output ("%da\n<<<<<<< %s\n", + D_LOWLINE (b, mapping[FILE0]) - 1, + type == DIFF_ALL ? file0 : file1); + leading_dot = 0; + if (type == DIFF_2ND) + { + /* Prepend lines from FILE1. */ + leading_dot = dotlines (b, mapping[FILE1]); + printf_output ("=======\n"); + } + undotlines (leading_dot, + D_LOWLINE (b, mapping[FILE0]) + 1, + D_NUMLINES (b, mapping[FILE1])); + } + else if (D_NUMLINES (b, mapping[FILE2]) == 0) + /* Write out a delete */ + { + if (D_NUMLINES (b, mapping[FILE0]) == 1) + printf_output ("%dd\n", D_LOWLINE (b, mapping[FILE0])); + else + printf_output ("%d,%dd\n", + D_LOWLINE (b, mapping[FILE0]), + D_HIGHLINE (b, mapping[FILE0])); + } + else + /* Write out an add or change */ + { + switch (D_NUMLINES (b, mapping[FILE0])) + { + case 0: + printf_output ("%da\n", D_HIGHLINE (b, mapping[FILE0])); + break; + case 1: + printf_output ("%dc\n", D_HIGHLINE (b, mapping[FILE0])); + break; + default: + printf_output ("%d,%dc\n", + D_LOWLINE (b, mapping[FILE0]), + D_HIGHLINE (b, mapping[FILE0])); + break; + } + + undotlines (dotlines (b, mapping[FILE2]), + D_LOWLINE (b, mapping[FILE0]), + D_NUMLINES (b, mapping[FILE2])); + } + } + if (finalwrite) printf_output ("w\nq\n"); + return conflicts_found; +} + +/* + * Read from INFILE and output to the standard output file a set of + * diff3_ blocks DIFF as a merged file. This acts like 'ed file0 + * <[output_diff3_edscript]', except that it works even for binary + * data or incomplete lines. + * + * As before, MAPPING maps from arg list file number to diff file number, + * REV_MAPPING is its inverse, + * and FILE0, FILE1, and FILE2 are the names of the files. + * + * Returns 1 if conflicts were found. + */ + +static int +output_diff3_merge (infile, diff, mapping, rev_mapping, + file0, file1, file2) + FILE *infile; + struct diff3_block *diff; + int const mapping[3], rev_mapping[3]; + char const *file0, *file1, *file2; +{ + int c, i; + char cc; + int conflicts_found = 0, conflict; + struct diff3_block *b; + int linesread = 0; + + for (b = diff; b; b = b->next) + { + /* Must do mapping correctly. */ + enum diff_type type + = ((b->correspond == DIFF_ALL) ? + DIFF_ALL : + ((enum diff_type) + (((int) DIFF_1ST) + + rev_mapping[(int) b->correspond - (int) DIFF_1ST]))); + char const *format_2nd = "<<<<<<< %s\n"; + + /* If we aren't supposed to do this output block, skip it. */ + switch (type) + { + default: continue; + case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break; + case DIFF_3RD: if (overlap_only) continue; conflict = 0; break; + case DIFF_ALL: if (simple_only) continue; conflict = flagging; + format_2nd = "||||||| %s\n"; + break; + } + + /* Copy I lines from file 0. */ + i = D_LOWLINE (b, FILE0) - linesread - 1; + linesread += i; + while (0 <= --i) + do + { + c = getc (infile); + if (c == EOF) + if (ferror (infile)) + diff3_perror_with_exit ("input file"); + else if (feof (infile)) + diff3_fatal ("input file shrank"); + cc = c; + write_output (&cc, 1); + } + while (c != '\n'); + + if (conflict) + { + conflicts_found = 1; + + if (type == DIFF_ALL) + { + /* Put in lines from FILE0 with bracket. */ + printf_output ("<<<<<<< %s\n", file0); + for (i = 0; + i < D_NUMLINES (b, mapping[FILE0]); + i++) + write_output (D_RELNUM (b, mapping[FILE0], i), + D_RELLEN (b, mapping[FILE0], i)); + } + + if (show_2nd) + { + /* Put in lines from FILE1 with bracket. */ + printf_output (format_2nd, file1); + for (i = 0; + i < D_NUMLINES (b, mapping[FILE1]); + i++) + write_output (D_RELNUM (b, mapping[FILE1], i), + D_RELLEN (b, mapping[FILE1], i)); + } + + printf_output ("=======\n"); + } + + /* Put in lines from FILE2. */ + for (i = 0; + i < D_NUMLINES (b, mapping[FILE2]); + i++) + write_output (D_RELNUM (b, mapping[FILE2], i), + D_RELLEN (b, mapping[FILE2], i)); + + if (conflict) + printf_output (">>>>>>> %s\n", file2); + + /* Skip I lines in file 0. */ + i = D_NUMLINES (b, FILE0); + linesread += i; + while (0 <= --i) + while ((c = getc (infile)) != '\n') + if (c == EOF) + if (ferror (infile)) + diff3_perror_with_exit ("input file"); + else if (feof (infile)) + { + if (i || b->next) + diff3_fatal ("input file shrank"); + return conflicts_found; + } + } + /* Copy rest of common file. */ + while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile))) + { + cc = c; + write_output (&cc, 1); + } + return conflicts_found; +} + +/* + * Reverse the order of the list of diff3 blocks. + */ +static struct diff3_block * +reverse_diff3_blocklist (diff) + struct diff3_block *diff; +{ + register struct diff3_block *tmp, *next, *prev; + + for (tmp = diff, prev = 0; tmp; tmp = next) + { + next = tmp->next; + tmp->next = prev; + prev = tmp; + } + + return prev; +} + +static size_t +myread (fd, ptr, size) + int fd; + char *ptr; + size_t size; +{ + ssize_t result = read (fd, ptr, size); + if (result == -1) + diff3_perror_with_exit ("read failed"); + return (size_t)result; +} + +static void +diff3_fatal (string) + char const *string; +{ + diff_error ("%s", string, 0); + DIFF3_ABORT (2); +} + +static void +diff3_perror_with_exit (string) + char const *string; +{ + perror_with_name (string); + DIFF3_ABORT (2); +} + +static void +initialize_main (argcp, argvp) + int *argcp; + char ***argvp; +{ + always_text = 0; + edscript = 0; + flagging = 0; + tab_align_flag = 0; + simple_only = 0; + overlap_only = 0; + show_2nd = 0; + finalwrite = 0; + merge = 0; + diff_program_name = (*argvp)[0]; + outfile = NULL; +} + +static void +free_diff_blocks(p) + struct diff_block *p; +{ + register struct diff_block *next; + + while (p) + { + next = p->next; + if (p->lines[0]) free(p->lines[0]); + if (p->lines[1]) free(p->lines[1]); + if (p->lengths[0]) free(p->lengths[0]); + if (p->lengths[1]) free(p->lengths[1]); + free(p); + p = next; + } +} + +static void +free_diff3_blocks(p) + struct diff3_block *p; +{ + register struct diff3_block *next; + + while (p) + { + next = p->next; + if (p->lines[0]) free(p->lines[0]); + if (p->lines[1]) free(p->lines[1]); + if (p->lines[2]) free(p->lines[2]); + if (p->lengths[0]) free(p->lengths[0]); + if (p->lengths[1]) free(p->lengths[1]); + if (p->lengths[2]) free(p->lengths[2]); + free(p); + p = next; + } +} diff --git a/contrib/cvs-1.12.9/diff/diffrun.h b/contrib/cvs-1.12.9/diff/diffrun.h new file mode 100644 index 0000000000..9ee804b2b6 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/diffrun.h @@ -0,0 +1,69 @@ +/* Interface header file for GNU DIFF library. + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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. + +*/ + +#ifndef DIFFRUN_H +#define DIFFRUN_H + +/* This header file defines the interfaces used by the diff library. + It should be included by programs which use the diff library. */ + +#include + +#if defined __STDC__ && __STDC__ +#define DIFFPARAMS(args) args +#else +#define DIFFPARAMS(args) () +#endif + +/* The diff_callbacks structure is used to handle callbacks from the + diff library. All output goes through these callbacks. When a + pointer to this structure is passed in, it may be NULL. Also, any + of the individual callbacks may be NULL. This means that the + default action should be taken. */ + +struct diff_callbacks +{ + /* Write output. This function just writes a string of a given + length to the output file. The default is to fwrite to OUTFILE. + If this callback is defined, flush_output must also be defined. + If the length is zero, output zero bytes. */ + void (*write_output) DIFFPARAMS((char const *, size_t)); + /* Flush output. The default is to fflush OUTFILE. If this + callback is defined, write_output must also be defined. */ + void (*flush_output) DIFFPARAMS((void)); + /* Write a '\0'-terminated string to stdout. + This is called for version and help messages. */ + void (*write_stdout) DIFFPARAMS((char const *)); + /* Print an error message. The first argument is a printf format, + and the next two are parameters. The default is to print a + message on stderr. */ + void (*error) DIFFPARAMS((char const *, char const *, char const *)); +}; + +/* Run a diff. */ + +extern int diff_run DIFFPARAMS((int, char **, const char *, + const struct diff_callbacks *)); + +/* Run a diff3. */ + +extern int diff3_run DIFFPARAMS((int, char **, char *, + const struct diff_callbacks *)); + +#undef DIFFPARAMS + +#endif /* DIFFRUN_H */ diff --git a/contrib/cvs-1.12.9/diff/dir.c b/contrib/cvs-1.12.9/diff/dir.c new file mode 100644 index 0000000000..da497dc4a6 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/dir.c @@ -0,0 +1,218 @@ +/* Read, sort and compare two directories. Used for GNU DIFF. + Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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 "diff.h" + +/* Read the directory named by DIR and store into DIRDATA a sorted vector + of filenames for its contents. DIR->desc == -1 means this directory is + known to be nonexistent, so set DIRDATA to an empty vector. + Return -1 (setting errno) if error, 0 otherwise. */ + +struct dirdata +{ + char const **names; /* Sorted names of files in dir, 0-terminated. */ + char *data; /* Allocated storage for file names. */ +}; + +static int compare_names PARAMS((void const *, void const *)); +static int dir_sort PARAMS((struct file_data const *, struct dirdata *)); + +#ifdef _WIN32 +#define CLOSEDIR_VOID 1 +#endif + +static int +dir_sort (dir, dirdata) + struct file_data const *dir; + struct dirdata *dirdata; +{ + register struct dirent *next; + register int i; + + /* Address of block containing the files that are described. */ + char const **names; + + /* Number of files in directory. */ + size_t nnames; + + /* Allocated and used storage for file name data. */ + char *data; + size_t data_alloc, data_used; + + dirdata->names = 0; + dirdata->data = 0; + nnames = 0; + data = 0; + + if (dir->desc != -1) + { + /* Open the directory and check for errors. */ + register DIR *reading = CVS_OPENDIR (dir->name); + if (!reading) + return -1; + + /* Initialize the table of filenames. */ + + data_alloc = max (1, (size_t) dir->stat.st_size); + data_used = 0; + dirdata->data = data = xmalloc (data_alloc); + + /* Read the directory entries, and insert the subfiles + into the `data' table. */ + + while ((errno = 0, (next = CVS_READDIR (reading)) != 0)) + { + char *d_name = next->d_name; + size_t d_size = NAMLEN (next) + 1; + + /* Ignore the files `.' and `..' */ + if (d_name[0] == '.' + && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0))) + continue; + + if (excluded_filename (d_name)) + continue; + + while (data_alloc < data_used + d_size) + dirdata->data = data = xrealloc (data, data_alloc *= 2); + memcpy (data + data_used, d_name, d_size); + data_used += d_size; + nnames++; + } + if (errno) + { + int e = errno; + CVS_CLOSEDIR (reading); + errno = e; + return -1; + } +#if CLOSEDIR_VOID + CVS_CLOSEDIR (reading); +#else + if (CVS_CLOSEDIR (reading) != 0) + return -1; +#endif + } + + /* Create the `names' table from the `data' table. */ + dirdata->names = names = (char const **) xmalloc (sizeof (char *) + * (nnames + 1)); + for (i = 0; i < nnames; i++) + { + names[i] = data; + data += strlen (data) + 1; + } + names[nnames] = 0; + + /* Sort the table. */ + qsort (names, nnames, sizeof (char *), compare_names); + + return 0; +} + +/* Sort the files now in the table. */ + +static int +compare_names (file1, file2) + void const *file1, *file2; +{ + return filename_cmp (* (char const *const *) file1, + * (char const *const *) file2); +} + +/* Compare the contents of two directories named in FILEVEC[0] and FILEVEC[1]. + This is a top-level routine; it does everything necessary for diff + on two directories. + + FILEVEC[0].desc == -1 says directory FILEVEC[0] doesn't exist, + but pretend it is empty. Likewise for FILEVEC[1]. + + HANDLE_FILE is a caller-provided subroutine called to handle each file. + It gets five operands: dir and name (rel to original working dir) of file + in dir 0, dir and name pathname of file in dir 1, and the recursion depth. + + For a file that appears in only one of the dirs, one of the name-args + to HANDLE_FILE is zero. + + DEPTH is the current depth in recursion, used for skipping top-level + files by the -S option. + + Returns the maximum of all the values returned by HANDLE_FILE, + or 2 if trouble is encountered in opening files. */ + +int +diff_dirs (filevec, handle_file, depth) + struct file_data const filevec[]; + int (*handle_file) PARAMS((char const *, char const *, char const *, char const *, int)); + int depth; +{ + struct dirdata dirdata[2]; + int val = 0; /* Return value. */ + int i; + + /* Get sorted contents of both dirs. */ + for (i = 0; i < 2; i++) + if (dir_sort (&filevec[i], &dirdata[i]) != 0) + { + perror_with_name (filevec[i].name); + val = 2; + } + + if (val == 0) + { + register char const * const *names0 = dirdata[0].names; + register char const * const *names1 = dirdata[1].names; + char const *name0 = filevec[0].name; + char const *name1 = filevec[1].name; + + /* If `-S name' was given, and this is the topmost level of comparison, + ignore all file names less than the specified starting name. */ + + if (dir_start_file && depth == 0) + { + while (*names0 && filename_cmp (*names0, dir_start_file) < 0) + names0++; + while (*names1 && filename_cmp (*names1, dir_start_file) < 0) + names1++; + } + + /* Loop while files remain in one or both dirs. */ + while (*names0 || *names1) + { + /* Compare next name in dir 0 with next name in dir 1. + At the end of a dir, + pretend the "next name" in that dir is very large. */ + int nameorder = (!*names0 ? 1 : !*names1 ? -1 + : filename_cmp (*names0, *names1)); + int v1 = (*handle_file) (name0, 0 < nameorder ? 0 : *names0++, + name1, nameorder < 0 ? 0 : *names1++, + depth + 1); + if (v1 > val) + val = v1; + } + } + + for (i = 0; i < 2; i++) + { + if (dirdata[i].names) + free (dirdata[i].names); + if (dirdata[i].data) + free (dirdata[i].data); + } + + return val; +} diff --git a/contrib/cvs-1.12.9/diff/ed.c b/contrib/cvs-1.12.9/diff/ed.c new file mode 100644 index 0000000000..74fc2a4f1f --- /dev/null +++ b/contrib/cvs-1.12.9/diff/ed.c @@ -0,0 +1,198 @@ +/* Output routines for ed-script format. + Copyright (C) 1988, 89, 91, 92, 93, 1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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 "diff.h" + +static void print_ed_hunk PARAMS((struct change *)); +static void print_rcs_hunk PARAMS((struct change *)); +static void pr_forward_ed_hunk PARAMS((struct change *)); + +/* Print our script as ed commands. */ + +void +print_ed_script (script) + struct change *script; +{ + print_script (script, find_reverse_change, print_ed_hunk); +} + +/* Print a hunk of an ed diff */ + +static void +print_ed_hunk (hunk) + struct change *hunk; +{ + int f0, l0, f1, l1; + int deletes, inserts; + +#if 0 + hunk = flip_script (hunk); +#endif +#ifdef DEBUG + debug_script (hunk); +#endif + + /* Determine range of line numbers involved in each file. */ + analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts); + if (!deletes && !inserts) + return; + + begin_output (); + + /* Print out the line number header for this hunk */ + print_number_range (',', &files[0], f0, l0); + printf_output ("%c\n", change_letter (inserts, deletes)); + + /* Print new/changed lines from second file, if needed */ + if (inserts) + { + int i; + int inserting = 1; + for (i = f1; i <= l1; i++) + { + /* Resume the insert, if we stopped. */ + if (! inserting) + printf_output ("%da\n", + i - f1 + translate_line_number (&files[0], f0) - 1); + inserting = 1; + + /* If the file's line is just a dot, it would confuse `ed'. + So output it with a double dot, and set the flag LEADING_DOT + so that we will output another ed-command later + to change the double dot into a single dot. */ + + if (files[1].linbuf[i][0] == '.' + && files[1].linbuf[i][1] == '\n') + { + printf_output ("..\n"); + printf_output (".\n"); + /* Now change that double dot to the desired single dot. */ + printf_output ("%ds/^\\.\\././\n", + i - f1 + translate_line_number (&files[0], f0)); + inserting = 0; + } + else + /* Line is not `.', so output it unmodified. */ + print_1_line ("", &files[1].linbuf[i]); + } + + /* End insert mode, if we are still in it. */ + if (inserting) + printf_output (".\n"); + } +} + +/* Print change script in the style of ed commands, + but print the changes in the order they appear in the input files, + which means that the commands are not truly useful with ed. */ + +void +pr_forward_ed_script (script) + struct change *script; +{ + print_script (script, find_change, pr_forward_ed_hunk); +} + +static void +pr_forward_ed_hunk (hunk) + struct change *hunk; +{ + int i; + int f0, l0, f1, l1; + int deletes, inserts; + + /* Determine range of line numbers involved in each file. */ + analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts); + if (!deletes && !inserts) + return; + + begin_output (); + + printf_output ("%c", change_letter (inserts, deletes)); + print_number_range (' ', files, f0, l0); + printf_output ("\n"); + + /* If deletion only, print just the number range. */ + + if (!inserts) + return; + + /* For insertion (with or without deletion), print the number range + and the lines from file 2. */ + + for (i = f1; i <= l1; i++) + print_1_line ("", &files[1].linbuf[i]); + + printf_output (".\n"); +} + +/* Print in a format somewhat like ed commands + except that each insert command states the number of lines it inserts. + This format is used for RCS. */ + +void +print_rcs_script (script) + struct change *script; +{ + print_script (script, find_change, print_rcs_hunk); +} + +/* Print a hunk of an RCS diff */ + +static void +print_rcs_hunk (hunk) + struct change *hunk; +{ + int i; + int f0, l0, f1, l1; + int deletes, inserts; + int tf0, tl0, tf1, tl1; + + /* Determine range of line numbers involved in each file. */ + analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts); + if (!deletes && !inserts) + return; + + begin_output (); + + translate_range (&files[0], f0, l0, &tf0, &tl0); + + if (deletes) + { + printf_output ("d"); + /* For deletion, print just the starting line number from file 0 + and the number of lines deleted. */ + printf_output ("%d %d\n", + tf0, + (tl0 >= tf0 ? tl0 - tf0 + 1 : 1)); + } + + if (inserts) + { + printf_output ("a"); + + /* Take last-line-number from file 0 and # lines from file 1. */ + translate_range (&files[1], f1, l1, &tf1, &tl1); + printf_output ("%d %d\n", + tl0, + (tl1 >= tf1 ? tl1 - tf1 + 1 : 1)); + + /* Print the inserted lines. */ + for (i = f1; i <= l1; i++) + print_1_line ("", &files[1].linbuf[i]); + } +} diff --git a/contrib/cvs-1.12.9/diff/ifdef.c b/contrib/cvs-1.12.9/diff/ifdef.c new file mode 100644 index 0000000000..94fcfb5677 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/ifdef.c @@ -0,0 +1,436 @@ +/* #ifdef-format output routines for GNU DIFF. + Copyright (C) 1989, 1991, 1992, 1993, 1994, 1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU DIFF General Public +License for full details. + +Everyone is granted permission to copy, modify and redistribute +GNU DIFF, but only under the conditions described in the +GNU DIFF General Public License. A copy of this license is +supposed to have been given to you along with GNU DIFF so you +can know your rights and responsibilities. It should be in a +file named COPYING. Among other things, the copyright notice +and this notice must be preserved on all copies. */ + + +#include "diff.h" + +struct group +{ + struct file_data const *file; + int from, upto; /* start and limit lines for this group of lines */ +}; + +static char *format_group PARAMS((int, char *, int, struct group const *)); +static char *scan_char_literal PARAMS((char *, int *)); +static char *scan_printf_spec PARAMS((char *)); +static int groups_letter_value PARAMS((struct group const *, int)); +static void format_ifdef PARAMS((char *, int, int, int, int)); +static void print_ifdef_hunk PARAMS((struct change *)); +static void print_ifdef_lines PARAMS((int, char *, struct group const *)); + +static int next_line; + +/* Print the edit-script SCRIPT as a merged #ifdef file. */ + +void +print_ifdef_script (script) + struct change *script; +{ + next_line = - files[0].prefix_lines; + print_script (script, find_change, print_ifdef_hunk); + if (next_line < files[0].valid_lines) + { + begin_output (); + format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines, + next_line - files[0].valid_lines + files[1].valid_lines, + files[1].valid_lines); + } +} + +/* Print a hunk of an ifdef diff. + This is a contiguous portion of a complete edit script, + describing changes in consecutive lines. */ + +static void +print_ifdef_hunk (hunk) + struct change *hunk; +{ + int first0, last0, first1, last1, deletes, inserts; + char *format; + + /* Determine range of line numbers involved in each file. */ + analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts); + if (inserts) + format = deletes ? group_format[CHANGED] : group_format[NEW]; + else if (deletes) + format = group_format[OLD]; + else + return; + + begin_output (); + + /* Print lines up to this change. */ + if (next_line < first0) + format_ifdef (group_format[UNCHANGED], next_line, first0, + next_line - first0 + first1, first1); + + /* Print this change. */ + next_line = last0 + 1; + format_ifdef (format, first0, next_line, first1, last1 + 1); +} + +/* Print a set of lines according to FORMAT. + Lines BEG0 up to END0 are from the first file; + lines BEG1 up to END1 are from the second file. */ + +static void +format_ifdef (format, beg0, end0, beg1, end1) + char *format; + int beg0, end0, beg1, end1; +{ + struct group groups[2]; + + groups[0].file = &files[0]; + groups[0].from = beg0; + groups[0].upto = end0; + groups[1].file = &files[1]; + groups[1].from = beg1; + groups[1].upto = end1; + format_group (1, format, '\0', groups); +} + +/* If DOIT is non-zero, output a set of lines according to FORMAT. + The format ends at the first free instance of ENDCHAR. + Yield the address of the terminating character. + GROUPS specifies which lines to print. + If OUT is zero, do not actually print anything; just scan the format. */ + +static char * +format_group (doit, format, endchar, groups) + int doit; + char *format; + int endchar; + struct group const *groups; +{ + register char c; + register char *f = format; + + while ((c = *f) != endchar && c != 0) + { + f++; + if (c == '%') + { + char *spec = f; + switch ((c = *f++)) + { + case '%': + break; + + case '(': + /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */ + { + int i, value[2]; + int thendoit, elsedoit; + + for (i = 0; i < 2; i++) + { + unsigned char f0 = f[0]; + if (ISDIGIT (f0)) + { + value[i] = atoi (f); + while (ISDIGIT ((unsigned char) *++f)) + continue; + } + else + { + value[i] = groups_letter_value (groups, f0); + if (value[i] < 0) + goto bad_format; + f++; + } + if (*f++ != "=?"[i]) + goto bad_format; + } + if (value[0] == value[1]) + thendoit = doit, elsedoit = 0; + else + thendoit = 0, elsedoit = doit; + f = format_group (thendoit, f, ':', groups); + if (*f) + { + f = format_group (elsedoit, f + 1, ')', groups); + if (*f) + f++; + } + } + continue; + + case '<': + /* Print lines deleted from first file. */ + print_ifdef_lines (doit, line_format[OLD], &groups[0]); + continue; + + case '=': + /* Print common lines. */ + print_ifdef_lines (doit, line_format[UNCHANGED], &groups[0]); + continue; + + case '>': + /* Print lines inserted from second file. */ + print_ifdef_lines (doit, line_format[NEW], &groups[1]); + continue; + + default: + { + int value; + char *speclim; + + f = scan_printf_spec (spec); + if (!f) + goto bad_format; + speclim = f; + c = *f++; + switch (c) + { + case '\'': + f = scan_char_literal (f, &value); + if (!f) + goto bad_format; + break; + + default: + value = groups_letter_value (groups, c); + if (value < 0) + goto bad_format; + break; + } + if (doit) + { + /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */ + *speclim = 0; + printf_output (spec - 1, value); + /* Undo the temporary replacement. */ + *speclim = c; + } + } + continue; + + bad_format: + c = '%'; + f = spec; + break; + } + } + if (doit) + { + /* Don't take the address of a register variable. */ + char cc = c; + write_output (&cc, 1); + } + } + return f; +} + +/* For the line group pair G, return the number corresponding to LETTER. + Return -1 if LETTER is not a group format letter. */ +static int +groups_letter_value (g, letter) + struct group const *g; + int letter; +{ + if (ISUPPER (letter)) + { + g++; + letter = tolower (letter); + } + switch (letter) + { + case 'e': return translate_line_number (g->file, g->from) - 1; + case 'f': return translate_line_number (g->file, g->from); + case 'l': return translate_line_number (g->file, g->upto) - 1; + case 'm': return translate_line_number (g->file, g->upto); + case 'n': return g->upto - g->from; + default: return -1; + } +} + +/* Output using FORMAT to print the line group GROUP. + But do nothing if DOIT is zero. */ +static void +print_ifdef_lines (doit, format, group) + int doit; + char *format; + struct group const *group; +{ + struct file_data const *file = group->file; + char const * const *linbuf = file->linbuf; + int from = group->from, upto = group->upto; + + if (!doit) + return; + + /* If possible, use a single fwrite; it's faster. */ + if (!tab_expand_flag && format[0] == '%') + { + if (format[1] == 'l' && format[2] == '\n' && !format[3]) + { + write_output (linbuf[from], + (linbuf[upto] + (linbuf[upto][-1] != '\n') + - linbuf[from])); + return; + } + if (format[1] == 'L' && !format[2]) + { + write_output (linbuf[from], + linbuf[upto] - linbuf[from]); + return; + } + } + + for (; from < upto; from++) + { + register char c; + register char *f = format; + char cc; + + while ((c = *f++) != 0) + { + if (c == '%') + { + char *spec = f; + switch ((c = *f++)) + { + case '%': + break; + + case 'l': + output_1_line (linbuf[from], + linbuf[from + 1] + - (linbuf[from + 1][-1] == '\n'), 0, 0); + continue; + + case 'L': + output_1_line (linbuf[from], linbuf[from + 1], 0, 0); + continue; + + default: + { + int value; + char *speclim; + + f = scan_printf_spec (spec); + if (!f) + goto bad_format; + speclim = f; + c = *f++; + switch (c) + { + case '\'': + f = scan_char_literal (f, &value); + if (!f) + goto bad_format; + break; + + case 'n': + value = translate_line_number (file, from); + break; + + default: + goto bad_format; + } + /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */ + *speclim = 0; + printf_output (spec - 1, value); + /* Undo the temporary replacement. */ + *speclim = c; + } + continue; + + bad_format: + c = '%'; + f = spec; + break; + } + } + + /* Don't take the address of a register variable. */ + cc = c; + write_output (&cc, 1); + } + } +} + +/* Scan the character literal represented in the string LIT; LIT points just + after the initial apostrophe. Put the literal's value into *INTPTR. + Yield the address of the first character after the closing apostrophe, + or zero if the literal is ill-formed. */ +static char * +scan_char_literal (lit, intptr) + char *lit; + int *intptr; +{ + register char *p = lit; + int value, digits; + char c = *p++; + + switch (c) + { + case 0: + case '\'': + return 0; + + case '\\': + value = 0; + while ((c = *p++) != '\'') + { + unsigned digit = c - '0'; + if (8 <= digit) + return 0; + value = 8 * value + digit; + } + digits = p - lit - 2; + if (! (1 <= digits && digits <= 3)) + return 0; + break; + + default: + value = c; + if (*p++ != '\'') + return 0; + break; + } + *intptr = value; + return p; +} + +/* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'. + Return the address of the character following SPEC, or zero if failure. */ +static char * +scan_printf_spec (spec) + register char *spec; +{ + register unsigned char c; + + while ((c = *spec++) == '-') + continue; + while (ISDIGIT (c)) + c = *spec++; + if (c == '.') + while (ISDIGIT (c = *spec++)) + continue; + switch (c) + { + case 'c': case 'd': case 'o': case 'x': case 'X': + return spec; + + default: + return 0; + } +} diff --git a/contrib/cvs-1.12.9/diff/io.c b/contrib/cvs-1.12.9/diff/io.c new file mode 100644 index 0000000000..31581cdb0b --- /dev/null +++ b/contrib/cvs-1.12.9/diff/io.c @@ -0,0 +1,711 @@ +/* File I/O for GNU DIFF. + Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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 "diff.h" + +/* Rotate a value n bits to the left. */ +#define UINT_BIT (sizeof (unsigned) * CHAR_BIT) +#define ROL(v, n) ((v) << (n) | (v) >> (UINT_BIT - (n))) + +/* Given a hash value and a new character, return a new hash value. */ +#define HASH(h, c) ((c) + ROL (h, 7)) + +/* Guess remaining number of lines from number N of lines so far, + size S so far, and total size T. */ +#define GUESS_LINES(n,s,t) (((t) - (s)) / ((n) < 10 ? 32 : (s) / ((n)-1)) + 5) + +/* Type used for fast prefix comparison in find_identical_ends. */ +#ifndef word +#define word int +#endif + +/* Lines are put into equivalence classes (of lines that match in line_cmp). + Each equivalence class is represented by one of these structures, + but only while the classes are being computed. + Afterward, each class is represented by a number. */ +struct equivclass +{ + int next; /* Next item in this bucket. */ + unsigned hash; /* Hash of lines in this class. */ + char const *line; /* A line that fits this class. */ + size_t length; /* That line's length, not counting its newline. */ +}; + +/* Hash-table: array of buckets, each being a chain of equivalence classes. + buckets[-1] is reserved for incomplete lines. */ +static int *buckets; + +/* Number of buckets in the hash table array, not counting buckets[-1]. */ +static int nbuckets; + +/* Array in which the equivalence classes are allocated. + The bucket-chains go through the elements in this array. + The number of an equivalence class is its index in this array. */ +static struct equivclass *equivs; + +/* Index of first free element in the array `equivs'. */ +static int equivs_index; + +/* Number of elements allocated in the array `equivs'. */ +static int equivs_alloc; + +static void find_and_hash_each_line PARAMS((struct file_data *)); +static void find_identical_ends PARAMS((struct file_data[])); +static void prepare_text_end PARAMS((struct file_data *)); + +/* Check for binary files and compare them for exact identity. */ + +/* Return 1 if BUF contains a non text character. + SIZE is the number of characters in BUF. */ + +#define binary_file_p(buf, size) (memchr (buf, '\0', size) != 0) + +/* Get ready to read the current file. + Return nonzero if SKIP_TEST is zero, + and if it appears to be a binary file. */ + +int +sip (current, skip_test) + struct file_data *current; + int skip_test; +{ + /* If we have a nonexistent file at this stage, treat it as empty. */ + if (current->desc < 0) + { + /* Leave room for a sentinel. */ + current->bufsize = sizeof (word); + current->buffer = xmalloc (current->bufsize); + } + else + { + current->bufsize = STAT_BLOCKSIZE (current->stat); + current->buffer = xmalloc (current->bufsize); + + if (! skip_test) + { + /* Check first part of file to see if it's a binary file. */ +#if HAVE_SETMODE + int oldmode = setmode (current->desc, O_BINARY); +#endif + ssize_t n = read (current->desc, current->buffer, current->bufsize); + if (n == -1) + pfatal_with_name (current->name); + current->buffered_chars = n; +#if HAVE_SETMODE + if (oldmode != O_BINARY) + { + if (lseek (current->desc, - (off_t) n, SEEK_CUR) == -1) + pfatal_with_name (current->name); + setmode (current->desc, oldmode); + current->buffered_chars = 0; + } +#endif + return binary_file_p (current->buffer, n); + } + } + + current->buffered_chars = 0; + return 0; +} + +/* Slurp the rest of the current file completely into memory. */ + +void +slurp (current) + struct file_data *current; +{ + ssize_t cc; + + if (current->desc < 0) + /* The file is nonexistent. */ + ; + else if (S_ISREG (current->stat.st_mode)) + { + /* It's a regular file; slurp in the rest all at once. */ + + /* Get the size out of the stat block. + Allocate enough room for appended newline and sentinel. */ + cc = current->stat.st_size + 1 + sizeof (word); + if (current->bufsize < cc) + { + current->bufsize = cc; + current->buffer = xrealloc (current->buffer, cc); + } + + if (current->buffered_chars < current->stat.st_size) + { + cc = read (current->desc, + current->buffer + current->buffered_chars, + current->stat.st_size - current->buffered_chars); + if (cc == -1) + pfatal_with_name (current->name); + current->buffered_chars += cc; + } + } + /* It's not a regular file; read it, growing the buffer as needed. */ + else if (always_text_flag || current->buffered_chars != 0) + { + for (;;) + { + if (current->buffered_chars == current->bufsize) + { + current->bufsize = current->bufsize * 2; + current->buffer = xrealloc (current->buffer, current->bufsize); + } + cc = read (current->desc, + current->buffer + current->buffered_chars, + current->bufsize - current->buffered_chars); + if (cc == 0) + break; + if (cc == -1) + pfatal_with_name (current->name); + current->buffered_chars += cc; + } + /* Allocate just enough room for appended newline and sentinel. */ + current->bufsize = current->buffered_chars + 1 + sizeof (word); + current->buffer = xrealloc (current->buffer, current->bufsize); + } +} + +/* Split the file into lines, simultaneously computing the equivalence class for + each line. */ + +static void +find_and_hash_each_line (current) + struct file_data *current; +{ + unsigned h; + unsigned char const *p = (unsigned char const *) current->prefix_end; + unsigned char c; + int i, *bucket; + size_t length; + + /* Cache often-used quantities in local variables to help the compiler. */ + char const **linbuf = current->linbuf; + int alloc_lines = current->alloc_lines; + int line = 0; + int linbuf_base = current->linbuf_base; + int *cureqs = (int *) xmalloc (alloc_lines * sizeof (int)); + struct equivclass *eqs = equivs; + int eqs_index = equivs_index; + int eqs_alloc = equivs_alloc; + char const *suffix_begin = current->suffix_begin; + char const *bufend = current->buffer + current->buffered_chars; + int use_line_cmp = ignore_some_line_changes; + + while ((char const *) p < suffix_begin) + { + char const *ip = (char const *) p; + + /* Compute the equivalence class for this line. */ + + h = 0; + + /* Hash this line until we find a newline. */ + if (ignore_case_flag) + { + if (ignore_all_space_flag) + while ((c = *p++) != '\n') + { + if (! ISSPACE (c)) + h = HASH (h, ISUPPER (c) ? tolower (c) : c); + } + else if (ignore_space_change_flag) + while ((c = *p++) != '\n') + { + if (ISSPACE (c)) + { + for (;;) + { + c = *p++; + if (!ISSPACE (c)) + break; + if (c == '\n') + goto hashing_done; + } + h = HASH (h, ' '); + } + /* C is now the first non-space. */ + h = HASH (h, ISUPPER (c) ? tolower (c) : c); + } + else + while ((c = *p++) != '\n') + h = HASH (h, ISUPPER (c) ? tolower (c) : c); + } + else + { + if (ignore_all_space_flag) + while ((c = *p++) != '\n') + { + if (! ISSPACE (c)) + h = HASH (h, c); + } + else if (ignore_space_change_flag) + while ((c = *p++) != '\n') + { + if (ISSPACE (c)) + { + for (;;) + { + c = *p++; + if (!ISSPACE (c)) + break; + if (c == '\n') + goto hashing_done; + } + h = HASH (h, ' '); + } + /* C is now the first non-space. */ + h = HASH (h, c); + } + else + while ((c = *p++) != '\n') + h = HASH (h, c); + } + hashing_done:; + + bucket = &buckets[h % nbuckets]; + length = (char const *) p - ip - 1; + + if ((char const *) p == bufend + && current->missing_newline + && ROBUST_OUTPUT_STYLE (output_style)) + { + /* This line is incomplete. If this is significant, + put the line into bucket[-1]. */ + if (! (ignore_space_change_flag | ignore_all_space_flag)) + bucket = &buckets[-1]; + + /* Omit the inserted newline when computing linbuf later. */ + p--; + bufend = suffix_begin = (char const *) p; + } + + for (i = *bucket; ; i = eqs[i].next) + if (!i) + { + /* Create a new equivalence class in this bucket. */ + i = eqs_index++; + if (i == eqs_alloc) + eqs = (struct equivclass *) + xrealloc (eqs, (eqs_alloc*=2) * sizeof(*eqs)); + eqs[i].next = *bucket; + eqs[i].hash = h; + eqs[i].line = ip; + eqs[i].length = length; + *bucket = i; + break; + } + else if (eqs[i].hash == h) + { + char const *eqline = eqs[i].line; + + /* Reuse existing equivalence class if the lines are identical. + This detects the common case of exact identity + faster than complete comparison would. */ + if (eqs[i].length == length && memcmp (eqline, ip, length) == 0) + break; + + /* Reuse existing class if line_cmp reports the lines equal. */ + if (use_line_cmp && line_cmp (eqline, ip) == 0) + break; + } + + /* Maybe increase the size of the line table. */ + if (line == alloc_lines) + { + /* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */ + alloc_lines = 2 * alloc_lines - linbuf_base; + cureqs = (int *) xrealloc (cureqs, alloc_lines * sizeof (*cureqs)); + linbuf = (char const **) xrealloc (linbuf + linbuf_base, + (alloc_lines - linbuf_base) + * sizeof (*linbuf)) + - linbuf_base; + } + linbuf[line] = ip; + cureqs[line] = i; + ++line; + } + + current->buffered_lines = line; + + for (i = 0; ; i++) + { + /* Record the line start for lines in the suffix that we care about. + Record one more line start than lines, + so that we can compute the length of any buffered line. */ + if (line == alloc_lines) + { + /* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */ + alloc_lines = 2 * alloc_lines - linbuf_base; + linbuf = (char const **) xrealloc (linbuf + linbuf_base, + (alloc_lines - linbuf_base) + * sizeof (*linbuf)) + - linbuf_base; + } + linbuf[line] = (char const *) p; + + if ((char const *) p == bufend) + break; + + if (context <= i && no_diff_means_no_output) + break; + + line++; + + while (*p++ != '\n') + ; + } + + /* Done with cache in local variables. */ + current->linbuf = linbuf; + current->valid_lines = line; + current->alloc_lines = alloc_lines; + current->equivs = cureqs; + equivs = eqs; + equivs_alloc = eqs_alloc; + equivs_index = eqs_index; +} + +/* Prepare the end of the text. Make sure it's initialized. + Make sure text ends in a newline, + but remember that we had to add one. */ + +static void +prepare_text_end (current) + struct file_data *current; +{ + size_t buffered_chars = current->buffered_chars; + char *p = current->buffer; + + if (buffered_chars == 0 || p[buffered_chars - 1] == '\n') + current->missing_newline = 0; + else + { + p[buffered_chars++] = '\n'; + current->buffered_chars = buffered_chars; + current->missing_newline = 1; + } + + /* Don't use uninitialized storage when planting or using sentinels. */ + if (p) + bzero (p + buffered_chars, sizeof (word)); +} + +/* Given a vector of two file_data objects, find the identical + prefixes and suffixes of each object. */ + +static void +find_identical_ends (filevec) + struct file_data filevec[]; +{ + word *w0, *w1; + char *p0, *p1, *buffer0, *buffer1; + char const *end0, *beg0; + char const **linbuf0, **linbuf1; + int i, lines; + size_t n0, n1, tem; + int alloc_lines0, alloc_lines1; + int buffered_prefix, prefix_count, prefix_mask; + + slurp (&filevec[0]); + if (filevec[0].desc != filevec[1].desc) + slurp (&filevec[1]); + else + { + filevec[1].buffer = filevec[0].buffer; + filevec[1].bufsize = filevec[0].bufsize; + filevec[1].buffered_chars = filevec[0].buffered_chars; + } + for (i = 0; i < 2; i++) + prepare_text_end (&filevec[i]); + + /* Find identical prefix. */ + + p0 = buffer0 = filevec[0].buffer; + p1 = buffer1 = filevec[1].buffer; + + n0 = filevec[0].buffered_chars; + n1 = filevec[1].buffered_chars; + + if (p0 == p1) + /* The buffers are the same; sentinels won't work. */ + p0 = p1 += n1; + else + { + /* Insert end sentinels, in this case characters that are guaranteed + to make the equality test false, and thus terminate the loop. */ + + if (n0 < n1) + p0[n0] = ~p1[n0]; + else + p1[n1] = ~p0[n1]; + + /* Loop until first mismatch, or to the sentinel characters. */ + + /* Compare a word at a time for speed. */ + w0 = (word *) p0; + w1 = (word *) p1; + while (*w0++ == *w1++) + ; + --w0, --w1; + + /* Do the last few bytes of comparison a byte at a time. */ + p0 = (char *) w0; + p1 = (char *) w1; + while (*p0++ == *p1++) + ; + --p0, --p1; + + /* Don't mistakenly count missing newline as part of prefix. */ + if (ROBUST_OUTPUT_STYLE (output_style) + && (buffer0 + n0 - filevec[0].missing_newline < p0) + != + (buffer1 + n1 - filevec[1].missing_newline < p1)) + --p0, --p1; + } + + /* Now P0 and P1 point at the first nonmatching characters. */ + + /* Skip back to last line-beginning in the prefix, + and then discard up to HORIZON_LINES lines from the prefix. */ + i = horizon_lines; + while (p0 != buffer0 && (p0[-1] != '\n' || i--)) + --p0, --p1; + + /* Record the prefix. */ + filevec[0].prefix_end = p0; + filevec[1].prefix_end = p1; + + /* Find identical suffix. */ + + /* P0 and P1 point beyond the last chars not yet compared. */ + p0 = buffer0 + n0; + p1 = buffer1 + n1; + + if (! ROBUST_OUTPUT_STYLE (output_style) + || filevec[0].missing_newline == filevec[1].missing_newline) + { + end0 = p0; /* Addr of last char in file 0. */ + + /* Get value of P0 at which we should stop scanning backward: + this is when either P0 or P1 points just past the last char + of the identical prefix. */ + beg0 = filevec[0].prefix_end + (n0 < n1 ? 0 : n0 - n1); + + /* Scan back until chars don't match or we reach that point. */ + for (; p0 != beg0; p0--, p1--) + if (*p0 != *p1) + { + /* Point at the first char of the matching suffix. */ + beg0 = p0; + break; + } + + /* Are we at a line-beginning in both files? If not, add the rest of + this line to the main body. Discard up to HORIZON_LINES lines from + the identical suffix. Also, discard one extra line, + because shift_boundaries may need it. */ + i = horizon_lines + !((buffer0 == p0 || p0[-1] == '\n') + && + (buffer1 == p1 || p1[-1] == '\n')); + while (i-- && p0 != end0) + while (*p0++ != '\n') + ; + + p1 += p0 - beg0; + } + + /* Record the suffix. */ + filevec[0].suffix_begin = p0; + filevec[1].suffix_begin = p1; + + /* Calculate number of lines of prefix to save. + + prefix_count == 0 means save the whole prefix; + we need this with for options like -D that output the whole file. + We also need it for options like -F that output some preceding line; + at least we will need to find the last few lines, + but since we don't know how many, it's easiest to find them all. + + Otherwise, prefix_count != 0. Save just prefix_count lines at start + of the line buffer; they'll be moved to the proper location later. + Handle 1 more line than the context says (because we count 1 too many), + rounded up to the next power of 2 to speed index computation. */ + + if (no_diff_means_no_output && ! function_regexp_list) + { + for (prefix_count = 1; prefix_count < context + 1; prefix_count *= 2) + ; + prefix_mask = prefix_count - 1; + alloc_lines0 + = prefix_count + + GUESS_LINES (0, 0, p0 - filevec[0].prefix_end) + + context; + } + else + { + prefix_count = 0; + prefix_mask = ~0; + alloc_lines0 = GUESS_LINES (0, 0, n0); + } + + lines = 0; + linbuf0 = (char const **) xmalloc (alloc_lines0 * sizeof (*linbuf0)); + + /* If the prefix is needed, find the prefix lines. */ + if (! (no_diff_means_no_output + && filevec[0].prefix_end == p0 + && filevec[1].prefix_end == p1)) + { + p0 = buffer0; + end0 = filevec[0].prefix_end; + while (p0 != end0) + { + int l = lines++ & prefix_mask; + if (l == alloc_lines0) + linbuf0 = (char const **) xrealloc (linbuf0, (alloc_lines0 *= 2) + * sizeof(*linbuf0)); + linbuf0[l] = p0; + while (*p0++ != '\n') + ; + } + } + buffered_prefix = prefix_count && context < lines ? context : lines; + + /* Allocate line buffer 1. */ + tem = prefix_count ? filevec[1].suffix_begin - buffer1 : n1; + + alloc_lines1 + = (buffered_prefix + + GUESS_LINES (lines, filevec[1].prefix_end - buffer1, tem) + + context); + linbuf1 = (char const **) xmalloc (alloc_lines1 * sizeof (*linbuf1)); + + if (buffered_prefix != lines) + { + /* Rotate prefix lines to proper location. */ + for (i = 0; i < buffered_prefix; i++) + linbuf1[i] = linbuf0[(lines - context + i) & prefix_mask]; + for (i = 0; i < buffered_prefix; i++) + linbuf0[i] = linbuf1[i]; + } + + /* Initialize line buffer 1 from line buffer 0. */ + for (i = 0; i < buffered_prefix; i++) + linbuf1[i] = linbuf0[i] - buffer0 + buffer1; + + /* Record the line buffer, adjusted so that + linbuf*[0] points at the first differing line. */ + filevec[0].linbuf = linbuf0 + buffered_prefix; + filevec[1].linbuf = linbuf1 + buffered_prefix; + filevec[0].linbuf_base = filevec[1].linbuf_base = - buffered_prefix; + filevec[0].alloc_lines = alloc_lines0 - buffered_prefix; + filevec[1].alloc_lines = alloc_lines1 - buffered_prefix; + filevec[0].prefix_lines = filevec[1].prefix_lines = lines; +} + +/* Largest primes less than some power of two, for nbuckets. Values range + from useful to preposterous. If one of these numbers isn't prime + after all, don't blame it on me, blame it on primes (6) . . . */ +static int const primes[] = +{ + 509, + 1021, + 2039, + 4093, + 8191, + 16381, + 32749, +#if 32767 < INT_MAX + 65521, + 131071, + 262139, + 524287, + 1048573, + 2097143, + 4194301, + 8388593, + 16777213, + 33554393, + 67108859, /* Preposterously large . . . */ + 134217689, + 268435399, + 536870909, + 1073741789, + 2147483647, +#endif + 0 +}; + +/* Given a vector of two file_data objects, read the file associated + with each one, and build the table of equivalence classes. + Return 1 if either file appears to be a binary file. + If PRETEND_BINARY is nonzero, pretend they are binary regardless. */ + +int +read_files (filevec, pretend_binary) + struct file_data filevec[]; + int pretend_binary; +{ + int i; + int skip_test = always_text_flag | pretend_binary; + int appears_binary = pretend_binary | sip (&filevec[0], skip_test); + + if (filevec[0].desc != filevec[1].desc) + appears_binary |= sip (&filevec[1], skip_test | appears_binary); + else + { + filevec[1].buffer = filevec[0].buffer; + filevec[1].bufsize = filevec[0].bufsize; + filevec[1].buffered_chars = filevec[0].buffered_chars; + } + if (appears_binary) + { +#if HAVE_SETMODE + setmode (filevec[0].desc, O_BINARY); + setmode (filevec[1].desc, O_BINARY); +#endif + return 1; + } + + find_identical_ends (filevec); + + equivs_alloc = filevec[0].alloc_lines + filevec[1].alloc_lines + 1; + equivs = (struct equivclass *) xmalloc (equivs_alloc * sizeof (struct equivclass)); + /* Equivalence class 0 is permanently safe for lines that were not + hashed. Real equivalence classes start at 1. */ + equivs_index = 1; + + for (i = 0; primes[i] < equivs_alloc / 3; i++) + if (! primes[i]) + abort (); + nbuckets = primes[i]; + + buckets = (int *) xmalloc ((nbuckets + 1) * sizeof (*buckets)); + bzero (buckets++, (nbuckets + 1) * sizeof (*buckets)); + + for (i = 0; i < 2; i++) + find_and_hash_each_line (&filevec[i]); + + filevec[0].equiv_max = filevec[1].equiv_max = equivs_index; + + free (equivs); + free (buckets - 1); + + return 0; +} diff --git a/contrib/cvs-1.12.9/diff/normal.c b/contrib/cvs-1.12.9/diff/normal.c new file mode 100644 index 0000000000..b1f495522e --- /dev/null +++ b/contrib/cvs-1.12.9/diff/normal.c @@ -0,0 +1,69 @@ +/* Normal-format output routines for GNU DIFF. + Copyright (C) 1988, 1989, 1993, 1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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 "diff.h" + +static void print_normal_hunk PARAMS((struct change *)); + +/* Print the edit-script SCRIPT as a normal diff. + INF points to an array of descriptions of the two files. */ + +void +print_normal_script (script) + struct change *script; +{ + print_script (script, find_change, print_normal_hunk); +} + +/* Print a hunk of a normal diff. + This is a contiguous portion of a complete edit script, + describing changes in consecutive lines. */ + +static void +print_normal_hunk (hunk) + struct change *hunk; +{ + int first0, last0, first1, last1, deletes, inserts; + register int i; + + /* Determine range of line numbers involved in each file. */ + analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts); + if (!deletes && !inserts) + return; + + begin_output (); + + /* Print out the line number header for this hunk */ + print_number_range (',', &files[0], first0, last0); + printf_output ("%c", change_letter (inserts, deletes)); + print_number_range (',', &files[1], first1, last1); + printf_output ("\n"); + + /* Print the lines that the first file has. */ + if (deletes) + for (i = first0; i <= last0; i++) + print_1_line ("<", &files[0].linbuf[i]); + + if (inserts && deletes) + printf_output ("---\n"); + + /* Print the lines that the second file has. */ + if (inserts) + for (i = first1; i <= last1; i++) + print_1_line (">", &files[1].linbuf[i]); +} diff --git a/contrib/cvs-1.12.9/diff/side.c b/contrib/cvs-1.12.9/diff/side.c new file mode 100644 index 0000000000..d776e77eab --- /dev/null +++ b/contrib/cvs-1.12.9/diff/side.c @@ -0,0 +1,294 @@ +/* sdiff-format output routines for GNU DIFF. + Copyright (C) 1991, 1992, 1993, 1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU DIFF General Public +License for full details. + +Everyone is granted permission to copy, modify and redistribute +GNU DIFF, but only under the conditions described in the +GNU DIFF General Public License. A copy of this license is +supposed to have been given to you along with GNU DIFF so you +can know your rights and responsibilities. It should be in a +file named COPYING. Among other things, the copyright notice +and this notice must be preserved on all copies. */ + + +#include "diff.h" + +static unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned)); +static unsigned tab_from_to PARAMS((unsigned, unsigned)); +static void print_1sdiff_line PARAMS((char const * const *, int, char const * const *)); +static void print_sdiff_common_lines PARAMS((int, int)); +static void print_sdiff_hunk PARAMS((struct change *)); + +/* Next line number to be printed in the two input files. */ +static int next0, next1; + +/* Print the edit-script SCRIPT as a sdiff style output. */ + +void +print_sdiff_script (script) + struct change *script; +{ + begin_output (); + + next0 = next1 = - files[0].prefix_lines; + print_script (script, find_change, print_sdiff_hunk); + + print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines); +} + +/* Tab from column FROM to column TO, where FROM <= TO. Yield TO. */ + +static unsigned +tab_from_to (from, to) + unsigned from, to; +{ + unsigned tab; + + if (! tab_expand_flag) + for (tab = from + TAB_WIDTH - from % TAB_WIDTH; tab <= to; tab += TAB_WIDTH) + { + write_output ("\t", 1); + from = tab; + } + while (from++ < to) + write_output (" ", 1); + return to; +} + +/* + * Print the text for half an sdiff line. This means truncate to width + * observing tabs, and trim a trailing newline. Returns the last column + * written (not the number of chars). + */ +static unsigned +print_half_line (line, indent, out_bound) + char const * const *line; + unsigned indent, out_bound; +{ + register unsigned in_position = 0, out_position = 0; + register char const + *text_pointer = line[0], + *text_limit = line[1]; + + while (text_pointer < text_limit) + { + register unsigned char c = *text_pointer++; + /* We use CC to avoid taking the address of the register + variable C. */ + char cc; + + switch (c) + { + case '\t': + { + unsigned spaces = TAB_WIDTH - in_position % TAB_WIDTH; + if (in_position == out_position) + { + unsigned tabstop = out_position + spaces; + if (tab_expand_flag) + { + if (out_bound < tabstop) + tabstop = out_bound; + for (; out_position < tabstop; out_position++) + write_output (" ", 1); + } + else + if (tabstop < out_bound) + { + out_position = tabstop; + cc = c; + write_output (&cc, 1); + } + } + in_position += spaces; + } + break; + + case '\r': + { + cc = c; + write_output (&cc, 1); + tab_from_to (0, indent); + in_position = out_position = 0; + } + break; + + case '\b': + if (in_position != 0 && --in_position < out_bound) + if (out_position <= in_position) + /* Add spaces to make up for suppressed tab past out_bound. */ + for (; out_position < in_position; out_position++) + write_output (" ", 1); + else + { + out_position = in_position; + cc = c; + write_output (&cc, 1); + } + break; + + case '\f': + case '\v': + control_char: + if (in_position < out_bound) + { + cc = c; + write_output (&cc, 1); + } + break; + + default: + if (! ISPRINT (c)) + goto control_char; + /* falls through */ + case ' ': + if (in_position++ < out_bound) + { + out_position = in_position; + cc = c; + write_output (&cc, 1); + } + break; + + case '\n': + return out_position; + } + } + + return out_position; +} + +/* + * Print side by side lines with a separator in the middle. + * 0 parameters are taken to indicate white space text. + * Blank lines that can easily be caught are reduced to a single newline. + */ + +static void +print_1sdiff_line (left, sep, right) + char const * const *left; + int sep; + char const * const *right; +{ + unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset; + unsigned col = 0; + int put_newline = 0; + + if (left) + { + if (left[1][-1] == '\n') + put_newline = 1; + col = print_half_line (left, 0, hw); + } + + if (sep != ' ') + { + char cc; + + col = tab_from_to (col, (hw + c2o - 1) / 2) + 1; + if (sep == '|' && put_newline != (right[1][-1] == '\n')) + sep = put_newline ? '/' : '\\'; + cc = sep; + write_output (&cc, 1); + } + + if (right) + { + if (right[1][-1] == '\n') + put_newline = 1; + if (**right != '\n') + { + col = tab_from_to (col, c2o); + print_half_line (right, col, hw); + } + } + + if (put_newline) + write_output ("\n", 1); +} + +/* Print lines common to both files in side-by-side format. */ +static void +print_sdiff_common_lines (limit0, limit1) + int limit0, limit1; +{ + int i0 = next0, i1 = next1; + + if (! sdiff_skip_common_lines && (i0 != limit0 || i1 != limit1)) + { + if (sdiff_help_sdiff) + printf_output ("i%d,%d\n", limit0 - i0, limit1 - i1); + + if (! sdiff_left_only) + { + while (i0 != limit0 && i1 != limit1) + print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]); + while (i1 != limit1) + print_1sdiff_line (0, ')', &files[1].linbuf[i1++]); + } + while (i0 != limit0) + print_1sdiff_line (&files[0].linbuf[i0++], '(', 0); + } + + next0 = limit0; + next1 = limit1; +} + +/* Print a hunk of an sdiff diff. + This is a contiguous portion of a complete edit script, + describing changes in consecutive lines. */ + +static void +print_sdiff_hunk (hunk) + struct change *hunk; +{ + int first0, last0, first1, last1, deletes, inserts; + register int i, j; + + /* Determine range of line numbers involved in each file. */ + analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts); + if (!deletes && !inserts) + return; + + /* Print out lines up to this change. */ + print_sdiff_common_lines (first0, first1); + + if (sdiff_help_sdiff) + printf_output ("c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1); + + /* Print ``xxx | xxx '' lines */ + if (inserts && deletes) + { + for (i = first0, j = first1; i <= last0 && j <= last1; ++i, ++j) + print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]); + deletes = i <= last0; + inserts = j <= last1; + next0 = first0 = i; + next1 = first1 = j; + } + + + /* Print `` > xxx '' lines */ + if (inserts) + { + for (j = first1; j <= last1; ++j) + print_1sdiff_line (0, '>', &files[1].linbuf[j]); + next1 = j; + } + + /* Print ``xxx < '' lines */ + if (deletes) + { + for (i = first0; i <= last0; ++i) + print_1sdiff_line (&files[0].linbuf[i], '<', 0); + next0 = i; + } +} diff --git a/contrib/cvs-1.12.9/diff/system.h b/contrib/cvs-1.12.9/diff/system.h new file mode 100644 index 0000000000..ad320f473d --- /dev/null +++ b/contrib/cvs-1.12.9/diff/system.h @@ -0,0 +1,302 @@ +/* System dependent declarations. + Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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. + +*/ + +/* We must define `volatile' and `const' first (the latter inside config.h), + so that they're used consistently in all system includes. */ +#if !__STDC__ +#ifndef volatile +#define volatile +#endif +#endif +#include + +#include +#include + +/* Note that PARAMS is just internal to the diff library; diffrun.h + has its own mechanism, which will hopefully be less likely to + conflict with the library's caller's namespace. */ +#if __STDC__ +#define PARAMS(args) args +#define VOID void +#else +#define PARAMS(args) () +#define VOID char +#endif + +#if STAT_MACROS_BROKEN +#undef S_ISBLK +#undef S_ISCHR +#undef S_ISDIR +#undef S_ISFIFO +#undef S_ISREG +#undef S_ISSOCK +#endif +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISBLK) && defined(S_IFBLK) +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISFIFO) && defined(S_IFFIFO) +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO) +#endif + +#ifndef S_ISSOCK +# if defined( S_IFSOCK ) +# ifdef S_IFMT +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +# else +# define S_ISSOCK(mode) ((mode) & S_IFSOCK) +# endif /* S_IFMT */ +# elif defined( S_ISNAM ) + /* SCO OpenServer 5.0.6a */ +# define S_ISSOCK S_ISNAM +# endif /* !S_IFSOCK && S_ISNAM */ +#endif /* !S_ISSOCK */ + +#if HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_IO_H +# include +#endif + +#ifdef HAVE_FCNTL_H +# include +#else +# include +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +/* I believe that all relevant systems have + time.h. It is in ANSI, for example. The + code below looks quite bogus as I don't think + sys/time.h is ever a substitute for time.h; + it is something different. */ +#define HAVE_TIME_H 1 + +#if HAVE_TIME_H +#include +#else +#include +#endif + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +#if HAVE_SYS_WAIT_H +#include +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +#ifndef STAT_BLOCKSIZE +#if HAVE_STRUCT_STAT_ST_BLKSIZE +#define STAT_BLOCKSIZE(s) (s).st_blksize +#else +#define STAT_BLOCKSIZE(s) (8 * 1024) +#endif +#endif + +#if HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) ((dirent)->d_namlen) +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +#if HAVE_VFORK_H +#include +#endif + +#if HAVE_STDLIB_H || defined(STDC_HEADERS) +#include +#else +VOID *malloc (); +VOID *realloc (); +#endif +#ifndef getenv +char *getenv (); +#endif + +#include +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#if STDC_HEADERS || HAVE_STRING_H +# include +# ifndef bzero +# define bzero(s, n) memset (s, 0, n) +# endif +#else +# if !HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr (), *strrchr (); +# if !HAVE_MEMCHR +# define memcmp(s1, s2, n) bcmp (s1, s2, n) +# define memcpy(d, s, n) bcopy (s, d, n) +void *memchr (); +# endif +#endif + +#include +/* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given + as an argument to macros like `isspace'. */ +#if STDC_HEADERS +#define CTYPE_DOMAIN(c) 1 +#else +#define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) +#endif +#ifndef ISPRINT +#define ISPRINT(c) (CTYPE_DOMAIN (c) && isprint (c)) +#endif +#ifndef ISSPACE +#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) +#endif +#ifndef ISUPPER +#define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c)) +#endif + +#ifndef ISDIGIT +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) +#endif + +#include +#if !STDC_HEADERS +extern int errno; +#endif + +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#define max(a,b) ((a) >= (b) ? (a) : (b)) + +/* This section contains Posix-compliant defaults for macros + that are meant to be overridden by hand in config.h as needed. */ + +#ifndef filename_cmp +#define filename_cmp(a, b) strcmp (a, b) +#endif + +#ifndef filename_lastdirchar +#define filename_lastdirchar(filename) strrchr (filename, '/') +#endif + +#ifndef HAVE_FORK +#define HAVE_FORK 1 +#endif + +#ifndef HAVE_SETMODE +#define HAVE_SETMODE 0 +#endif + +#ifndef initialize_main +#define initialize_main(argcp, argvp) +#endif + +/* Do struct stat *S, *T describe the same file? Answer -1 if unknown. */ +#ifndef same_file +#define same_file(s,t) ((s)->st_ino==(t)->st_ino && (s)->st_dev==(t)->st_dev) +#endif + +/* Place into Q a quoted version of A suitable for `popen' or `system', + incrementing Q and junking A. + Do not increment Q by more than 4 * strlen (A) + 2. */ +#ifndef SYSTEM_QUOTE_ARG +#define SYSTEM_QUOTE_ARG(q, a) \ + { \ + *(q)++ = '\''; \ + for (; *(a); *(q)++ = *(a)++) \ + if (*(a) == '\'') \ + { \ + *(q)++ = '\''; \ + *(q)++ = '\\'; \ + *(q)++ = '\''; \ + } \ + *(q)++ = '\''; \ + } +#endif + +/* these come from CVS's lib/system.h, but I wasn't sure how to include that + * properly or even if I really should + */ +#ifndef CVS_OPENDIR +#define CVS_OPENDIR opendir +#endif +#ifndef CVS_READDIR +#define CVS_READDIR readdir +#endif +#ifndef CVS_CLOSEDIR +#define CVS_CLOSEDIR closedir +#endif diff --git a/contrib/cvs-1.12.9/diff/util.c b/contrib/cvs-1.12.9/diff/util.c new file mode 100644 index 0000000000..744cf51949 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/util.c @@ -0,0 +1,849 @@ +/* Support routines for GNU DIFF. + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF 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. + +GNU DIFF 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 "diff.h" + +#if __STDC__ +#include +#else +#include +#endif + +#ifndef strerror +extern char *strerror (); +#endif + +/* Queue up one-line messages to be printed at the end, + when -l is specified. Each message is recorded with a `struct msg'. */ + +struct msg +{ + struct msg *next; + char const *format; + char const *arg1; + char const *arg2; + char const *arg3; + char const *arg4; +}; + +/* Head of the chain of queues messages. */ + +static struct msg *msg_chain; + +/* Tail of the chain of queues messages. */ + +static struct msg **msg_chain_end = &msg_chain; + +/* Use when a system call returns non-zero status. + TEXT should normally be the file name. */ + +void +perror_with_name (text) + char const *text; +{ + int e = errno; + + if (callbacks && callbacks->error) + (*callbacks->error) ("%s: %s", text, strerror (e)); + else + { + fprintf (stderr, "%s: ", diff_program_name); + errno = e; + perror (text); + } +} + +/* Use when a system call returns non-zero status and that is fatal. */ + +void +pfatal_with_name (text) + char const *text; +{ + int e = errno; + print_message_queue (); + if (callbacks && callbacks->error) + (*callbacks->error) ("%s: %s", text, strerror (e)); + else + { + fprintf (stderr, "%s: ", diff_program_name); + errno = e; + perror (text); + } + DIFF_ABORT (2); +} + +/* Print an error message from the format-string FORMAT + with args ARG1 and ARG2. */ + +void +diff_error (format, arg, arg1) + char const *format, *arg, *arg1; +{ + if (callbacks && callbacks->error) + (*callbacks->error) (format, arg, arg1); + else + { + fprintf (stderr, "%s: ", diff_program_name); + fprintf (stderr, format, arg, arg1); + fprintf (stderr, "\n"); + } +} + +/* Print an error message containing the string TEXT, then exit. */ + +void +fatal (m) + char const *m; +{ + print_message_queue (); + diff_error ("%s", m, 0); + DIFF_ABORT (2); +} + +/* Like printf, except if -l in effect then save the message and print later. + This is used for things like "binary files differ" and "Only in ...". */ + +void +message (format, arg1, arg2) + char const *format, *arg1, *arg2; +{ + message5 (format, arg1, arg2, 0, 0); +} + +void +message5 (format, arg1, arg2, arg3, arg4) + char const *format, *arg1, *arg2, *arg3, *arg4; +{ + if (paginate_flag) + { + struct msg *new = (struct msg *) xmalloc (sizeof (struct msg)); + new->format = format; + new->arg1 = concat (arg1, "", ""); + new->arg2 = concat (arg2, "", ""); + new->arg3 = arg3 ? concat (arg3, "", "") : 0; + new->arg4 = arg4 ? concat (arg4, "", "") : 0; + new->next = 0; + *msg_chain_end = new; + msg_chain_end = &new->next; + } + else + { + if (sdiff_help_sdiff) + write_output (" ", 1); + printf_output (format, arg1, arg2, arg3, arg4); + } +} + +/* Output all the messages that were saved up by calls to `message'. */ + +void +print_message_queue () +{ + struct msg *m; + + for (m = msg_chain; m; m = m->next) + printf_output (m->format, m->arg1, m->arg2, m->arg3, m->arg4); +} + +/* Call before outputting the results of comparing files NAME0 and NAME1 + to set up OUTFILE, the stdio stream for the output to go to. + + Usually, OUTFILE is just stdout. But when -l was specified + we fork off a `pr' and make OUTFILE a pipe to it. + `pr' then outputs to our stdout. */ + +static char const *current_name0; +static char const *current_name1; +static int current_depth; + +static int output_in_progress = 0; + +void +setup_output (name0, name1, depth) + char const *name0, *name1; + int depth; +{ + current_name0 = name0; + current_name1 = name1; + current_depth = depth; +} + +#if HAVE_FORK && defined (PR_PROGRAM) +static pid_t pr_pid; +#endif + +void +begin_output () +{ + char *name; + + if (output_in_progress) + return; + output_in_progress = 1; + + /* Construct the header of this piece of diff. */ + name = xmalloc (strlen (current_name0) + strlen (current_name1) + + strlen (switch_string) + 7); + /* Posix.2 section 4.17.6.1.1 specifies this format. But there is a + bug in the first printing (IEEE Std 1003.2-1992 p 251 l 3304): + it says that we must print only the last component of the pathnames. + This requirement is silly and does not match historical practice. */ + sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1); + + if (paginate_flag && callbacks && callbacks->write_output) + fatal ("can't paginate when using library callbacks"); + + if (paginate_flag) + { + /* Make OUTFILE a pipe to a subsidiary `pr'. */ + +#ifdef PR_PROGRAM + +# if HAVE_FORK + int pipes[2]; + + if (pipe (pipes) != 0) + pfatal_with_name ("pipe"); + + fflush (stdout); + + pr_pid = vfork (); + if (pr_pid < 0) + pfatal_with_name ("vfork"); + + if (pr_pid == 0) + { + close (pipes[1]); + if (pipes[0] != STDIN_FILENO) + { + if (dup2 (pipes[0], STDIN_FILENO) < 0) + pfatal_with_name ("dup2"); + close (pipes[0]); + } + + execl (PR_PROGRAM, PR_PROGRAM, "-f", "-h", name, 0); + pfatal_with_name (PR_PROGRAM); + } + else + { + close (pipes[0]); + outfile = fdopen (pipes[1], "w"); + if (!outfile) + pfatal_with_name ("fdopen"); + } +# else /* ! HAVE_FORK */ + char *command = xmalloc (4 * strlen (name) + strlen (PR_PROGRAM) + 10); + char *p; + char const *a = name; + sprintf (command, "%s -f -h ", PR_PROGRAM); + p = command + strlen (command); + SYSTEM_QUOTE_ARG (p, a); + *p = 0; + outfile = popen (command, "w"); + if (!outfile) + pfatal_with_name (command); + free (command); +# endif /* ! HAVE_FORK */ +#else + fatal ("This port does not support the --paginate option to diff."); +#endif + } + else + { + + /* If -l was not specified, output the diff straight to `stdout'. */ + + /* If handling multiple files (because scanning a directory), + print which files the following output is about. */ + if (current_depth > 0) + printf_output ("%s\n", name); + } + + free (name); + + /* A special header is needed at the beginning of context output. */ + switch (output_style) + { + case OUTPUT_CONTEXT: + print_context_header (files, 0); + break; + + case OUTPUT_UNIFIED: + print_context_header (files, 1); + break; + + default: + break; + } +} + +/* Call after the end of output of diffs for one file. + If -l was given, close OUTFILE and get rid of the `pr' subfork. */ + +void +finish_output () +{ + if (paginate_flag && outfile != 0 && outfile != stdout) + { +#ifdef PR_PROGRAM + int wstatus, w; + if (ferror (outfile)) + fatal ("write error"); +# if ! HAVE_FORK + wstatus = pclose (outfile); +# else /* HAVE_FORK */ + if (fclose (outfile) != 0) + pfatal_with_name ("write error"); + while ((w = waitpid (pr_pid, &wstatus, 0)) < 0 && errno == EINTR) + ; + if (w < 0) + pfatal_with_name ("waitpid"); +# endif /* HAVE_FORK */ + if (wstatus != 0) + fatal ("subsidiary pr failed"); +#else + fatal ("internal error in finish_output"); +#endif + } + + output_in_progress = 0; +} + +/* Write something to the output file. */ + +void +write_output (text, len) + char const *text; + size_t len; +{ + if (callbacks && callbacks->write_output) + (*callbacks->write_output) (text, len); + else if (len == 1) + putc (*text, outfile); + else + fwrite (text, sizeof (char), len, outfile); +} + +/* Printf something to the output file. */ + +#if __STDC__ +#define VA_START(args, lastarg) va_start(args, lastarg) +#else /* ! __STDC__ */ +#define VA_START(args, lastarg) va_start(args) +#endif /* __STDC__ */ + +void +#if __STDC__ +printf_output (const char *format, ...) +#else +printf_output (format, va_alist) + char const *format; + va_dcl +#endif +{ + va_list args; + + VA_START (args, format); + if (callbacks && callbacks->write_output) + { + /* We implement our own limited printf-like functionality (%s, %d, + and %c only). Callers who want something fancier can use + sprintf. */ + const char *p = format; + char *q; + char *str; + int num; + int ch; + char buf[100]; + + while ((q = strchr (p, '%')) != NULL) + { + static const char msg[] = + "\ninternal error: bad % in printf_output\n"; + (*callbacks->write_output) (p, q - p); + + switch (q[1]) + { + case 's': + str = va_arg (args, char *); + (*callbacks->write_output) (str, strlen (str)); + break; + case 'd': + num = va_arg (args, int); + sprintf (buf, "%d", num); + (*callbacks->write_output) (buf, strlen (buf)); + break; + case 'c': + ch = va_arg (args, int); + buf[0] = ch; + (*callbacks->write_output) (buf, 1); + break; + default: + (*callbacks->write_output) (msg, sizeof (msg) - 1); + /* Don't just keep going, because q + 1 might point to the + terminating '\0'. */ + goto out; + } + p = q + 2; + } + (*callbacks->write_output) (p, strlen (p)); + } + else + vfprintf (outfile, format, args); + out: + va_end (args); +} + +/* Flush the output file. */ + +void +flush_output () +{ + if (callbacks && callbacks->flush_output) + (*callbacks->flush_output) (); + else + fflush (outfile); +} + +/* Compare two lines (typically one from each input file) + according to the command line options. + For efficiency, this is invoked only when the lines do not match exactly + but an option like -i might cause us to ignore the difference. + Return nonzero if the lines differ. */ + +int +line_cmp (s1, s2) + char const *s1, *s2; +{ + register unsigned char const *t1 = (unsigned char const *) s1; + register unsigned char const *t2 = (unsigned char const *) s2; + + while (1) + { + register unsigned char c1 = *t1++; + register unsigned char c2 = *t2++; + + /* Test for exact char equality first, since it's a common case. */ + if (c1 != c2) + { + /* Ignore horizontal white space if -b or -w is specified. */ + + if (ignore_all_space_flag) + { + /* For -w, just skip past any white space. */ + while (ISSPACE (c1) && c1 != '\n') c1 = *t1++; + while (ISSPACE (c2) && c2 != '\n') c2 = *t2++; + } + else if (ignore_space_change_flag) + { + /* For -b, advance past any sequence of white space in line 1 + and consider it just one Space, or nothing at all + if it is at the end of the line. */ + if (ISSPACE (c1)) + { + while (c1 != '\n') + { + c1 = *t1++; + if (! ISSPACE (c1)) + { + --t1; + c1 = ' '; + break; + } + } + } + + /* Likewise for line 2. */ + if (ISSPACE (c2)) + { + while (c2 != '\n') + { + c2 = *t2++; + if (! ISSPACE (c2)) + { + --t2; + c2 = ' '; + break; + } + } + } + + if (c1 != c2) + { + /* If we went too far when doing the simple test + for equality, go back to the first non-white-space + character in both sides and try again. */ + if (c2 == ' ' && c1 != '\n' + && (unsigned char const *) s1 + 1 < t1 + && ISSPACE(t1[-2])) + { + --t1; + continue; + } + if (c1 == ' ' && c2 != '\n' + && (unsigned char const *) s2 + 1 < t2 + && ISSPACE(t2[-2])) + { + --t2; + continue; + } + } + } + + /* Lowercase all letters if -i is specified. */ + + if (ignore_case_flag) + { + if (ISUPPER (c1)) + c1 = tolower (c1); + if (ISUPPER (c2)) + c2 = tolower (c2); + } + + if (c1 != c2) + break; + } + if (c1 == '\n') + return 0; + } + + return (1); +} + +/* Find the consecutive changes at the start of the script START. + Return the last link before the first gap. */ + +struct change * +find_change (start) + struct change *start; +{ + return start; +} + +struct change * +find_reverse_change (start) + struct change *start; +{ + return start; +} + +/* Divide SCRIPT into pieces by calling HUNKFUN and + print each piece with PRINTFUN. + Both functions take one arg, an edit script. + + HUNKFUN is called with the tail of the script + and returns the last link that belongs together with the start + of the tail. + + PRINTFUN takes a subscript which belongs together (with a null + link at the end) and prints it. */ + +void +print_script (script, hunkfun, printfun) + struct change *script; + struct change * (*hunkfun) PARAMS((struct change *)); + void (*printfun) PARAMS((struct change *)); +{ + struct change *next = script; + + while (next) + { + struct change *this, *end; + + /* Find a set of changes that belong together. */ + this = next; + end = (*hunkfun) (next); + + /* Disconnect them from the rest of the changes, + making them a hunk, and remember the rest for next iteration. */ + next = end->link; + end->link = 0; +#ifdef DEBUG + debug_script (this); +#endif + + /* Print this hunk. */ + (*printfun) (this); + + /* Reconnect the script so it will all be freed properly. */ + end->link = next; + } +} + +/* Print the text of a single line LINE, + flagging it with the characters in LINE_FLAG (which say whether + the line is inserted, deleted, changed, etc.). */ + +void +print_1_line (line_flag, line) + char const *line_flag; + char const * const *line; +{ + char const *text = line[0], *limit = line[1]; /* Help the compiler. */ + char const *flag_format = 0; + + /* If -T was specified, use a Tab between the line-flag and the text. + Otherwise use a Space (as Unix diff does). + Print neither space nor tab if line-flags are empty. */ + + if (line_flag && *line_flag) + { + flag_format = tab_align_flag ? "%s\t" : "%s "; + printf_output (flag_format, line_flag); + } + + output_1_line (text, limit, flag_format, line_flag); + + if ((!line_flag || line_flag[0]) && limit[-1] != '\n') + printf_output ("\n\\ No newline at end of file\n"); +} + +/* Output a line from TEXT up to LIMIT. Without -t, output verbatim. + With -t, expand white space characters to spaces, and if FLAG_FORMAT + is nonzero, output it with argument LINE_FLAG after every + internal carriage return, so that tab stops continue to line up. */ + +void +output_1_line (text, limit, flag_format, line_flag) + char const *text, *limit, *flag_format, *line_flag; +{ + if (!tab_expand_flag) + write_output (text, limit - text); + else + { + register unsigned char c; + register char const *t = text; + register unsigned column = 0; + /* CC is used to avoid taking the address of the register + variable C. */ + char cc; + + while (t < limit) + switch ((c = *t++)) + { + case '\t': + { + unsigned spaces = TAB_WIDTH - column % TAB_WIDTH; + column += spaces; + do + write_output (" ", 1); + while (--spaces); + } + break; + + case '\r': + write_output ("\r", 1); + if (flag_format && t < limit && *t != '\n') + printf_output (flag_format, line_flag); + column = 0; + break; + + case '\b': + if (column == 0) + continue; + column--; + write_output ("\b", 1); + break; + + default: + if (ISPRINT (c)) + column++; + cc = c; + write_output (&cc, 1); + break; + } + } +} + +int +change_letter (inserts, deletes) + int inserts, deletes; +{ + if (!inserts) + return 'd'; + else if (!deletes) + return 'a'; + else + return 'c'; +} + +/* Translate an internal line number (an index into diff's table of lines) + into an actual line number in the input file. + The internal line number is LNUM. FILE points to the data on the file. + + Internal line numbers count from 0 starting after the prefix. + Actual line numbers count from 1 within the entire file. */ + +int +translate_line_number (file, lnum) + struct file_data const *file; + int lnum; +{ + return lnum + file->prefix_lines + 1; +} + +void +translate_range (file, a, b, aptr, bptr) + struct file_data const *file; + int a, b; + int *aptr, *bptr; +{ + *aptr = translate_line_number (file, a - 1) + 1; + *bptr = translate_line_number (file, b + 1) - 1; +} + +/* Print a pair of line numbers with SEPCHAR, translated for file FILE. + If the two numbers are identical, print just one number. + + Args A and B are internal line numbers. + We print the translated (real) line numbers. */ + +void +print_number_range (sepchar, file, a, b) + int sepchar; + struct file_data *file; + int a, b; +{ + int trans_a, trans_b; + translate_range (file, a, b, &trans_a, &trans_b); + + /* Note: we can have B < A in the case of a range of no lines. + In this case, we should print the line number before the range, + which is B. */ + if (trans_b > trans_a) + printf_output ("%d%c%d", trans_a, sepchar, trans_b); + else + printf_output ("%d", trans_b); +} + +/* Look at a hunk of edit script and report the range of lines in each file + that it applies to. HUNK is the start of the hunk, which is a chain + of `struct change'. The first and last line numbers of file 0 are stored in + *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1. + Note that these are internal line numbers that count from 0. + + If no lines from file 0 are deleted, then FIRST0 is LAST0+1. + + Also set *DELETES nonzero if any lines of file 0 are deleted + and set *INSERTS nonzero if any lines of file 1 are inserted. + If only ignorable lines are inserted or deleted, both are + set to 0. */ + +void +analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts) + struct change *hunk; + int *first0, *last0, *first1, *last1; + int *deletes, *inserts; +{ + int l0, l1, show_from, show_to; + int i; + int trivial = ignore_blank_lines_flag || ignore_regexp_list; + struct change *next; + + show_from = show_to = 0; + + *first0 = hunk->line0; + *first1 = hunk->line1; + + next = hunk; + do + { + l0 = next->line0 + next->deleted - 1; + l1 = next->line1 + next->inserted - 1; + show_from += next->deleted; + show_to += next->inserted; + + for (i = next->line0; i <= l0 && trivial; i++) + if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n') + { + struct regexp_list *r; + char const *line = files[0].linbuf[i]; + int len = files[0].linbuf[i + 1] - line; + + for (r = ignore_regexp_list; r; r = r->next) + if (0 <= re_search (&r->buf, line, len, 0, len, 0)) + break; /* Found a match. Ignore this line. */ + /* If we got all the way through the regexp list without + finding a match, then it's nontrivial. */ + if (!r) + trivial = 0; + } + + for (i = next->line1; i <= l1 && trivial; i++) + if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n') + { + struct regexp_list *r; + char const *line = files[1].linbuf[i]; + int len = files[1].linbuf[i + 1] - line; + + for (r = ignore_regexp_list; r; r = r->next) + if (0 <= re_search (&r->buf, line, len, 0, len, 0)) + break; /* Found a match. Ignore this line. */ + /* If we got all the way through the regexp list without + finding a match, then it's nontrivial. */ + if (!r) + trivial = 0; + } + } + while ((next = next->link) != 0); + + *last0 = l0; + *last1 = l1; + + /* If all inserted or deleted lines are ignorable, + tell the caller to ignore this hunk. */ + + if (trivial) + show_from = show_to = 0; + + *deletes = show_from; + *inserts = show_to; +} + +/* Concatenate three strings, returning a newly malloc'd string. */ + +char * +concat (s1, s2, s3) + char const *s1, *s2, *s3; +{ + size_t len = strlen (s1) + strlen (s2) + strlen (s3); + char *new = xmalloc (len + 1); + sprintf (new, "%s%s%s", s1, s2, s3); + return new; +} + +/* Yield the newly malloc'd pathname + of the file in DIR whose filename is FILE. */ + +char * +dir_file_pathname (dir, file) + char const *dir, *file; +{ + char const *p = filename_lastdirchar (dir); + return concat (dir, "/" + (p && !p[1]), file); +} + +void +debug_script (sp) + struct change *sp; +{ + fflush (stdout); + for (; sp; sp = sp->link) + fprintf (stderr, "%3d %3d delete %d insert %d\n", + sp->line0, sp->line1, sp->deleted, sp->inserted); + fflush (stderr); +} diff --git a/contrib/cvs-1.12.9/diff/version.c b/contrib/cvs-1.12.9/diff/version.c new file mode 100644 index 0000000000..343a098371 --- /dev/null +++ b/contrib/cvs-1.12.9/diff/version.c @@ -0,0 +1,5 @@ +/* Version number of GNU diff. */ + +#include + +char const diff_version_string[] = "2.7"; diff --git a/contrib/cvs-1.12.9/doc/cvs.1 b/contrib/cvs-1.12.9/doc/cvs.1 new file mode 100644 index 0000000000..d349eda3e7 --- /dev/null +++ b/contrib/cvs-1.12.9/doc/cvs.1 @@ -0,0 +1,3875 @@ +.\" This is the man page for CVS. It is auto-generated from the +.\" cvs.man.header, cvs.texinfo, & cvs.man.footer files. Please make changes +.\" there. A full copyright & license notice may also be found in cvs.texinfo. +.\" +.\" Copyright 2004 The Free Software Foundation, +.\" Derek R. Price, +.\" & Ximbiot +.\" +.\" This documentation 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 documentation 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 documentation; if not, write to the Free Software +.\" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.TH CVS 1 "\*(Dt" +.\" Full space in nroff; half space in troff +.de SP +.if n .sp +.if t .sp .5 +.. +.\" quoted command +.de ` +.RB ` "\|\\$1\|" '\\$2 +.. +.SH "NAME" +cvs \- Concurrent Versions System +.SH "SYNOPSIS" +.TP +\fBcvs\fP [ \fIcvs_options\fP ] +.I cvs_command +[ +.I command_options +] [ +.I command_args +] +.SH "NOTE" +.IX "revision control system" "\fLcvs\fR" +.IX cvs "" "\fLcvs\fP \- concurrent versions system" +.IX "concurrent versions system \- \fLcvs\fP" +.IX "release control system" "cvs command" "" "\fLcvs\fP \- concurrent versions system" +.IX "source control system" "cvs command" "" "\fLcvs\fP \- concurrent versions system" +.IX revisions "cvs command" "" "\fLcvs\fP \- source control" +This manpage is a summary of some of the features of +\fBcvs\fP. It is auto-generated from an appendix of the CVS manual. +For more in-depth documentation, please consult the +Cederqvist manual (via the +.B info CVS +command or otherwise, +as described in the SEE ALSO section of this manpage). Cross-references +in this man page refer to nodes in the same. +.SH "CVS commands" +.SS "Guide to CVS commands" +.SP +This appendix describes the overall structure of +\fBcvs\fR commands, and describes some commands in +detail (others are described elsewhere; for a quick +reference to \fBcvs\fR commands, see node `Invoking CVS\(aq in the CVS manual). +.SP +.SH "Structure" +.SS "Overall structure of CVS commands" +.IX "Structure" +.IX "CVS command structure" +.IX "Command structure" +.IX "Format of CVS commands" +.SP +The overall format of all \fBcvs\fR commands is: +.SP +.PD 0 +.SP +.IP "" 2 +cvs [ cvs_options ] cvs_command [ command_options ] [ command_args ] + +.PD +.IP "" 0 +.SP +.IP "" 0 +\fBcvs\fR +.IP "" 2 +The name of the \fBcvs\fR program. +.SP +.IP "" 0 +\fBcvs_options\fR +.IP "" 2 +Some options that affect all sub-commands of \fBcvs\fR. These are +described below. +.SP +.IP "" 0 +\fBcvs_command\fR +.IP "" 2 +One of several different sub-commands. Some of the commands have +aliases that can be used instead; those aliases are noted in the +reference manual for that command. There are only two situations +where you may omit \fBcvs_command\fR: \fBcvs -H\fR elicits a +list of available commands, and \fBcvs -v\fR displays version +information on \fBcvs\fR itself. +.SP +.IP "" 0 +\fBcommand_options\fR +.IP "" 2 +Options that are specific for the command. +.SP +.IP "" 0 +\fBcommand_args\fR +.IP "" 2 +Arguments to the commands. +.SP +There is unfortunately some confusion between +\fBcvs_options\fR and \fBcommand_options\fR. +When given as a \fBcvs_option\fR, some options only +affect some of the commands. When given as a +\fBcommand_option\fR it may have a different meaning, and +be accepted by more commands. In other words, do not +take the above categorization too seriously. Look at +the documentation instead. +.SP +.SH "Exit status" +.SS "CVS\(aqs exit status" +.IX "Exit status, of CVS" +.SP +\fBcvs\fR can indicate to the calling environment whether it +succeeded or failed by setting its \fIexit status\fR. +The exact way of testing the exit status will vary from +one operating system to another. For example in a unix +shell script the \fB$?\fR variable will be 0 if the +last command returned a successful exit status, or +greater than 0 if the exit status indicated failure. +.SP +If \fBcvs\fR is successful, it returns a successful status; +if there is an error, it prints an error message and +returns a failure status. The one exception to this is +the \fBcvs diff\fR command. It will return a +successful status if it found no differences, or a +failure status if there were differences or if there +was an error. Because this behavior provides no good +way to detect errors, in the future it is possible that +\fBcvs diff\fR will be changed to behave like the +other \fBcvs\fR commands. +.SP +.SH "~/.cvsrc" +.SS "Default options and the ~/.cvsrc file" +.IX "\&.cvsrc file" +.IX "Option defaults" +.SP +There are some \fBcommand_options\fR that are used so +often that you might have set up an alias or some other +means to make sure you always specify that option. One +example (the one that drove the implementation of the +\fB.cvsrc\fR support, actually) is that many people find the +default output of the \fBdiff\fR command to be very +hard to read, and that either context diffs or unidiffs +are much easier to understand. +.SP +The \fB~/.cvsrc\fR file is a way that you can add +default options to \fBcvs_commands\fR within cvs, +instead of relying on aliases or other shell scripts. +.SP +The format of the \fB~/.cvsrc\fR file is simple. The +file is searched for a line that begins with the same +name as the \fBcvs_command\fR being executed. If a +match is found, then the remainder of the line is split +up (at whitespace characters) into separate options and +added to the command arguments \fIbefore\fR any +options from the command line. +.SP +If a command has two names (e.g., \fBcheckout\fR and +\fBco\fR), the official name, not necessarily the one +used on the command line, will be used to match against +the file. So if this is the contents of the user\(aqs +\fB~/.cvsrc\fR file: +.SP +.PD 0 +.SP +.IP "" 2 +log -N +.IP "" 2 +diff -uN +.IP "" 2 +rdiff -u +.IP "" 2 +update -Pd +.IP "" 2 +checkout -P +.IP "" 2 +release -d + +.PD +.IP "" 0 +.SP +the command \fBcvs checkout foo\fR would have the +\fB-P\fR option added to the arguments, as well as +\fBcvs co foo\fR. +.SP +With the example file above, the output from \fBcvs +diff foobar\fR will be in unidiff format. \fBcvs diff +-c foobar\fR will provide context diffs, as usual. +Getting "old" format diffs would be slightly more +complicated, because \fBdiff\fR doesn\(aqt have an option +to specify use of the "old" format, so you would need +\fBcvs -f diff foobar\fR. +.SP +In place of the command name you can use \fBcvs\fR to +specify global options (see node `Global options\(aq in the CVS manual). For +example the following line in \fB.cvsrc\fR +.SP +.PD 0 +.SP +.IP "" 2 +cvs -z6 + +.PD +.IP "" 0 +.SP +causes \fBcvs\fR to use compression level 6. +.SP +.SH "Global options" +.IX "Options, global" +.IX "Global options" +.IX "Left-hand options" +.SP +The available \fBcvs_options\fR (that are given to the +left of \fBcvs_command\fR) are: +.SP +.IP "" 0 +\fB--allow-root=\fIrootdir\fB\fR +.IP "" 2 +Specify legal \fBcvsroot\fR directory. See +see node `Password authentication server\(aq in the CVS manual. +.SP +.IX "Authentication, stream" +.IX "Stream authentication" +.IP "" 0 +\fB-a\fR +.IP "" 2 +Authenticate all communication between the client and +the server. Only has an effect on the \fBcvs\fR client. +As of this writing, this is only implemented when using +a GSSAPI connection (see node `GSSAPI authenticated\(aq in the CVS manual). +Authentication prevents certain sorts of attacks +involving hijacking the active \fBtcp\fR connection. +Enabling authentication does not enable encryption. +.SP +.IX "RCSBIN, overriding" +.IX "Overriding RCSBIN" +.IP "" 0 +\fB-b \fIbindir\fB\fR +.IP "" 2 +In \fBcvs\fR 1.9.18 and older, this specified that +\fBrcs\fR programs are in the \fIbindir\fR directory. +Current versions of \fBcvs\fR do not run \fBrcs\fR +programs; for compatibility this option is accepted, +but it does nothing. +.SP +.IX "TMPDIR, overriding" +.IX "Overriding TMPDIR" +.IP "" 0 +\fB-T \fItempdir\fB\fR +.IP "" 2 +Use \fItempdir\fR as the directory where temporary files are +located. Overrides the setting of the \fB$TMPDIR\fR environment +variable and any precompiled directory. This parameter should be +specified as an absolute pathname. +(When running client/server, \fB-T\fR affects only the local process; +specifying \fB-T\fR for the client has no effect on the server and +vice versa.) +.SP +.IX "CVSROOT, overriding" +.IX "Overriding CVSROOT" +.IP "" 0 +\fB-d \fIcvs_root_directory\fB\fR +.IP "" 2 +Use \fIcvs_root_directory\fR as the root directory +pathname of the repository. Overrides the setting of +the \fB$CVSROOT\fR environment variable. see node `Repository\(aq in the CVS manual. +.SP +.IX "EDITOR, overriding" +.IX "Overriding EDITOR" +.IP "" 0 +\fB-e \fIeditor\fB\fR +.IP "" 2 +Use \fIeditor\fR to enter revision log information. Overrides the +setting of the \fB$CVSEDITOR\fR and \fB$EDITOR\fR +environment variables. For more information, see +see node `Committing your changes\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-f\fR +.IP "" 2 +Do not read the \fB~/.cvsrc\fR file. This +option is most often used because of the +non-orthogonality of the \fBcvs\fR option set. For +example, the \fBcvs log\fR option \fB-N\fR (turn off +display of tag names) does not have a corresponding +option to turn the display on. So if you have +\fB-N\fR in the \fB~/.cvsrc\fR entry for \fBlog\fR, +you may need to use \fB-f\fR to show the tag names. +.SP +.IP "" 0 +\fB-H\fR +.IP "" 2 +.IP "" 0 +\fB--help\fR +.IP "" 2 +Display usage information about the specified \fBcvs_command\fR +(but do not actually execute the command). If you don\(aqt specify +a command name, \fBcvs -H\fR displays overall help for +\fBcvs\fR, including a list of other help options. +.SP +.IX "Read-only repository mode" +.IP "" 0 +\fB-R\fR +.IP "" 2 +Turns on read-only repository mode. This allows one to check out from a +read-only repository, such as within an anoncvs server, or from a \fBcd-rom\fR +repository. +.SP +Same effect as if the \fBCVSREADONLYFS\fR environment +variable is set. Using \fB-R\fR can also considerably +speed up checkouts over NFS. +.SP +.IX "Read-only mode" +.IP "" 0 +\fB-n\fR +.IP "" 2 +Do not change any files. Attempt to execute the +\fBcvs_command\fR, but only to issue reports; do not remove, +update, or merge any existing files, or create any new files. +.SP +Note that \fBcvs\fR will not necessarily produce exactly +the same output as without \fB-n\fR. In some cases +the output will be the same, but in other cases +\fBcvs\fR will skip some of the processing that would +have been required to produce the exact same output. +.SP +.IP "" 0 +\fB-Q\fR +.IP "" 2 +Cause the command to be really quiet; the command will only +generate output for serious problems. +.SP +.IP "" 0 +\fB-q\fR +.IP "" 2 +Cause the command to be somewhat quiet; informational messages, +such as reports of recursion through subdirectories, are +suppressed. +.SP +.IX "Read-only files, and -r" +.IP "" 0 +\fB-r\fR +.IP "" 2 +Make new working files read-only. Same effect +as if the \fB$CVSREAD\fR environment variable is set +(see node `Environment variables\(aq in the CVS manual). The default is to +make working files writable, unless watches are on +(see node `Watches\(aq in the CVS manual). +.SP +.IP "" 0 +\fB-s \fIvariable\fB=\fIvalue\fB\fR +.IP "" 2 +Set a user variable (see node `Variables\(aq in the CVS manual). +.SP +.IX "Trace" +.IP "" 0 +\fB-t\fR +.IP "" 2 +Trace program execution; display messages showing the steps of +\fBcvs\fR activity. Particularly useful with \fB-n\fR to explore the +potential impact of an unfamiliar command. +.SP +.IP "" 0 +\fB-v\fR +.IP "" 2 +.IP "" 0 +\fB--version\fR +.IP "" 2 +Display version and copyright information for \fBcvs\fR. +.SP +.IX "CVSREAD, overriding" +.IX "Overriding CVSREAD" +.IP "" 0 +\fB-w\fR +.IP "" 2 +Make new working files read-write. Overrides the +setting of the \fB$CVSREAD\fR environment variable. +Files are created read-write by default, unless \fB$CVSREAD\fR is +set or \fB-r\fR is given. +.SP +.IP "" 0 +\fB-x\fR +.IP "" 2 +.IX "Encryption" +Encrypt all communication between the client and the +server. Only has an effect on the \fBcvs\fR client. As +of this writing, this is only implemented when using a +GSSAPI connection (see node `GSSAPI authenticated\(aq in the CVS manual) or a +Kerberos connection (see node `Kerberos authenticated\(aq in the CVS manual). +Enabling encryption implies that message traffic is +also authenticated. Encryption support is not +available by default; it must be enabled using a +special configure option, \fB--enable-encryption\fR, +when you build \fBcvs\fR. +.SP +.IP "" 0 +\fB-z \fIgzip-level\fB\fR +.IP "" 2 +.IX "Compression" +.IX "Gzip" +Set the compression level. +Valid levels are 1 (high speed, low compression) to +9 (low speed, high compression), or 0 to disable +compression (the default). +Only has an effect on the \fBcvs\fR client. +.SP +.SP +.SH "Common options" +.SS "Common command options" +.IX "Common options" +.IX "Right-hand options" +.SP +This section describes the \fBcommand_options\fR that +are available across several \fBcvs\fR commands. These +options are always given to the right of +\fBcvs_command\fR. Not all +commands support all of these options; each option is +only supported for commands where it makes sense. +However, when a command has one of these options you +can almost always count on the same behavior of the +option as in other commands. (Other command options, +which are listed with the individual commands, may have +different behavior from one \fBcvs\fR command to the other). +.SP +\fBNote: the \fBhistory\fB command is an exception; it supports +many options that conflict even with these standard options.\fR +.SP +.IX "Dates" +.IX "Time" +.IX "Specifying dates" +.IP "" 0 +\fB-D \fIdate_spec\fB\fR +.IP "" 2 +Use the most recent revision no later than \fIdate_spec\fR. +\fIdate_spec\fR is a single argument, a date description +specifying a date in the past. +.SP +The specification is \fIsticky\fR when you use it to make a +private copy of a source file; that is, when you get a working +file using \fB-D\fR, \fBcvs\fR 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, see node `Sticky tags\(aq in the CVS manual). +.SP +\fB-D\fR is available with the \fBannotate\fR, \fBcheckout\fR, +\fBdiff\fR, \fBexport\fR, \fBhistory\fR, \fBls\fR, +\fBrdiff\fR, \fBrls\fR, \fBrtag\fR, \fBtag\fR, and \fBupdate\fR commands. +(The \fBhistory\fR command uses this option in a +slightly different way; see node `history options\(aq in the CVS manual). +.SP +.IX "Timezone, in input" +.IX "Zone, time, in input" +A wide variety of date formats are supported by +\fBcvs\fR. The most standard ones are ISO8601 (from the +International Standards Organization) and the Internet +e-mail standard (specified in RFC822 as amended by +RFC1123). +.SP +ISO8601 dates have many variants but a few examples +are: +.SP +.PD 0 +.SP +.IP "" 4 +1972-09-24 +.IP "" 4 +1972-09-24 20:05 + +.PD +.IP "" 2 +.SP +There are a lot more ISO8601 date formats, and \fBcvs\fR +accepts many of them, but you probably don\(aqt want to +hear the \fIwhole\fR long story :-). +.SP +In addition to the dates allowed in Internet e-mail +itself, \fBcvs\fR also allows some of the fields to be +omitted. For example: +.SP +.PD 0 +.SP +.IP "" 4 +24 Sep 1972 20:05 +.IP "" 4 +24 Sep + +.PD +.IP "" 2 +.SP +The date is interpreted as being in the +local timezone, unless a specific timezone is +specified. +.SP +These two date formats are preferred. However, +\fBcvs\fR currently accepts a wide variety of other date +formats. They are intentionally not documented here in +any detail, and future versions of \fBcvs\fR might not +accept all of them. +.SP +One such format is +\fB\fImonth\fB/\fIday\fB/\fIyear\fB\fR. This may +confuse people who are accustomed to having the month +and day in the other order; \fB1/4/96\fR is January 4, +not April 1. +.SP +Remember to quote the argument to the \fB-D\fR +flag so that your shell doesn\(aqt interpret spaces as +argument separators. A command using the \fB-D\fR +flag can look like this: +.SP +.PD 0 +.SP +.IP "" 4 +$ cvs diff -D "1 hour ago" cvs.texinfo + +.PD +.IP "" 2 +.SP +.IX "Forcing a tag match" +.IP "" 0 +\fB-f\fR +.IP "" 2 +When you specify a particular date or tag to \fBcvs\fR commands, they +normally ignore files that do not contain the tag (or did not +exist prior to the date) that you specified. Use the \fB-f\fR option +if you want files retrieved even when there is no match for the +tag or date. (The most recent revision of the file +will be used). +.SP +Note that even with \fB-f\fR, a tag that you specify +must exist (that is, in some file, not necessary in +every file). This is so that \fBcvs\fR will continue to +give an error if you mistype a tag name. +.SP +\fB-f\fR is available with these commands: +\fBannotate\fR, \fBcheckout\fR, \fBexport\fR, +\fBrdiff\fR, \fBrtag\fR, and \fBupdate\fR. +.SP +\fBWARNING: The \fBcommit\fB and \fBremove\fB +commands also have a +\fB-f\fB option, but it has a different behavior for +those commands. See see node `commit options\(aq in the CVS manual, and +see node `Removing files\(aq in the CVS manual.\fR +.SP +.IP "" 0 +\fB-k \fIkflag\fB\fR +.IP "" 2 +Override the default processing of RCS keywords other than +\fB-kb\fR. see node `Keyword substitution\(aq in the CVS manual, for the meaning of +\fIkflag\fR. Used with the \fBcheckout\fR and \fBupdate\fR +commands, your \fIkflag\fR specification is +\fIsticky\fR; that is, when you use this option +with a \fBcheckout\fR or \fBupdate\fR command, +\fBcvs\fR associates your selected \fIkflag\fR with any files +it operates on, and continues to use that \fIkflag\fR with future +commands on the same files until you specify otherwise. +.SP +The \fB-k\fR option is available with the \fBadd\fR, +\fBcheckout\fR, \fBdiff\fR, \fBexport\fR, \fBimport\fR and +\fBupdate\fR commands. +.SP +\fBWARNING: Prior to CVS version 1.12.2, the \fB-k\fB flag +overrode the \fB-kb\fB indication for a binary file. This could +sometimes corrupt binary files. see node `Merging and keywords\(aq in the CVS manual, for +more.\fR +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local; run only in current working directory, rather than +recursing through subdirectories. +.SP +Available with the following commands: \fBannotate\fR, \fBcheckout\fR, +\fBcommit\fR, \fBdiff\fR, \fBedit\fR, \fBeditors\fR, \fBexport\fR, +\fBlog\fR, \fBrdiff\fR, \fBremove\fR, \fBrtag\fR, +\fBstatus\fR, \fBtag\fR, \fBunedit\fR, \fBupdate\fR, \fBwatch\fR, +and \fBwatchers\fR. +.SP +.IX "Editor, avoiding invocation of" +.IX "Avoiding editor invocation" +.IP "" 0 +\fB-m \fImessage\fB\fR +.IP "" 2 +Use \fImessage\fR as log information, instead of +invoking an editor. +.SP +Available with the following commands: \fBadd\fR, +\fBcommit\fR and \fBimport\fR. +.SP +.IP "" 0 +\fB-n\fR +.IP "" 2 +Do not run any tag program. (A program can be +specified to run in the modules +database (see node `modules\(aq in the CVS manual); this option bypasses it). +.SP +\fBNote: this is not the same as the \fBcvs -n\fB +program option, which you can specify to the left of a cvs command!\fR +.SP +Available with the \fBcheckout\fR, \fBcommit\fR, \fBexport\fR, +and \fBrtag\fR commands. +.SP +.IP "" 0 +\fB-P\fR +.IP "" 2 +Prune empty directories. See see node `Removing directories\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-p\fR +.IP "" 2 +Pipe the files retrieved from the repository to standard output, +rather than writing them in the current directory. Available +with the \fBcheckout\fR and \fBupdate\fR commands. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Process directories recursively. This is the default for all \fBcvs\fR +commands, with the exception of \fBls\fR & \fBrls\fR. +.SP +Available with the following commands: \fBannotate\fR, \fBcheckout\fR, +\fBcommit\fR, \fBdiff\fR, \fBedit\fR, \fBeditors\fR, \fBexport\fR, +\fBls\fR, \fBrdiff\fR, \fBremove\fR, \fBrls\fR, \fBrtag\fR, +\fBstatus\fR, \fBtag\fR, \fBunedit\fR, \fBupdate\fR, \fBwatch\fR, +and \fBwatchers\fR. +.SP +.IP "" 0 +\fB-r \fItag\fB\fR +.IP "" 2 +.IX "HEAD, special tag" +.IX "BASE, special tag" +Use the revision specified by the \fItag\fR argument instead of the +default \fIhead\fR revision. As well as arbitrary tags defined +with the \fBtag\fR or \fBrtag\fR command, two special tags are +always available: \fBHEAD\fR refers to the most recent version +available in the repository, and \fBBASE\fR refers to the +revision you last checked out into the current working directory. +.SP +The tag specification is sticky when you use this +with \fBcheckout\fR or \fBupdate\fR to make your own +copy of a file: \fBcvs\fR remembers the tag and continues to use it on +future update commands, until you specify otherwise (for more information +on sticky tags/dates, see node `Sticky tags\(aq in the CVS manual). +.SP +The tag can be either a symbolic or numeric tag, as +described in see node `Tags\(aq in the CVS manual, or the name of a branch, as +described in see node `Branching and merging\(aq in the CVS manual. +.SP +Specifying the \fB-q\fR global option along with the +\fB-r\fR command option is often useful, to suppress +the warning messages when the \fBrcs\fR file +does not contain the specified tag. +.SP +\fBNote: this is not the same as the overall \fBcvs -r\fB option, +which you can specify to the left of a \fBcvs\fB command!\fR +.SP +\fB-r\fR is available with the \fBcheckout\fR, \fBcommit\fR, +\fBdiff\fR, \fBhistory\fR, \fBexport\fR, \fBrdiff\fR, +\fBrtag\fR, and \fBupdate\fR commands. +.SP +.IP "" 0 +\fB-W\fR +.IP "" 2 +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 \fB.cvswrappers\fR file. +Available with the following commands: \fBimport\fR, +and \fBupdate\fR. +.SP +.SP +.SH "admin" +.SS "Administration" +.IX "Admin (subcommand)" +.SP +.IP "\(bu" 2 +Requires: repository, working directory. +.IP "\(bu" 2 +Changes: repository. +.IP "\(bu" 2 +Synonym: rcs +.SP +This is the \fBcvs\fR interface to assorted +administrative facilities. Some of them have +questionable usefulness for \fBcvs\fR but exist for +historical purposes. Some of the questionable options +are likely to disappear in the future. This command +\fIdoes\fR work recursively, so extreme care should be +used. +.SP +.IX "cvsadmin" +.IX "UserAdminOptions, in CVSROOT/config" +On unix, if there is a group named \fBcvsadmin\fR, +only members of that group can run \fBcvs admin\fR +commands, except for those specified using the +\fBUserAdminOptions\fR configuration option in the +\fBCVSROOT/config\fR file. Options specified using +\fBUserAdminOptions\fR can be run by any user. See +see node `config\(aq in the CVS manual for more on \fBUserAdminOptions\fR. +.SP +The \fBcvsadmin\fR group should exist on the server, +or any system running the non-client/server \fBcvs\fR. +To disallow \fBcvs admin\fR for all users, create a +group with no users in it. On NT, the \fBcvsadmin\fR +feature does not exist and all users +can run \fBcvs admin\fR. +.SP +.SH "admin options" +.SP +Some of these options have questionable usefulness for +\fBcvs\fR but exist for historical purposes. Some even +make it impossible to use \fBcvs\fR until you undo the +effect! +.SP +.IP "" 0 +\fB-A\fIoldfile\fB\fR +.IP "" 2 +Might not work together with \fBcvs\fR. Append the +access list of \fIoldfile\fR to the access list of the +\fBrcs\fR file. +.SP +.IP "" 0 +\fB-a\fIlogins\fB\fR +.IP "" 2 +Might not work together with \fBcvs\fR. Append the +login names appearing in the comma-separated list +\fIlogins\fR to the access list of the \fBrcs\fR file. +.SP +.IP "" 0 +\fB-b[\fIrev\fB]\fR +.IP "" 2 +Set the default branch to \fIrev\fR. In \fBcvs\fR, you +normally do not manipulate default branches; sticky +tags (see node `Sticky tags\(aq in the CVS manual) are a better way to decide +which branch you want to work on. There is one reason +to run \fBcvs admin -b\fR: to revert to the vendor\(aqs +version when using vendor branches (see node `Reverting +local changes\(aq in the CVS manual). +There can be no space between \fB-b\fR and its argument. +.SP +.IX "Comment leader" +.IP "" 0 +\fB-c\fIstring\fB\fR +.IP "" 2 +Sets the comment leader to \fIstring\fR. The comment +leader is not used by current versions of \fBcvs\fR or +\fBrcs\fR 5.7. Therefore, you can almost surely not +worry about it. see node `Keyword substitution\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-e[\fIlogins\fB]\fR +.IP "" 2 +Might not work together with \fBcvs\fR. Erase the login +names appearing in the comma-separated list +\fIlogins\fR from the access list of the RCS file. If +\fIlogins\fR is omitted, erase the entire access list. +There can be no space between \fB-e\fR and its argument. +.SP +.IP "" 0 +\fB-I\fR +.IP "" 2 +Run interactively, even if the standard input is not a +terminal. This option does not work with the +client/server \fBcvs\fR and is likely to disappear in +a future release of \fBcvs\fR. +.SP +.IP "" 0 +\fB-i\fR +.IP "" 2 +Useless with \fBcvs\fR. This creates and initializes a +new \fBrcs\fR file, without depositing a revision. With +\fBcvs\fR, add files with the \fBcvs add\fR command +(see node `Adding files\(aq in the CVS manual). +.SP +.IP "" 0 +\fB-k\fIsubst\fB\fR +.IP "" 2 +Set the default keyword +substitution to \fIsubst\fR. see node `Keyword +substitution\(aq in the CVS manual. Giving an explicit \fB-k\fR option to +\fBcvs update\fR, \fBcvs export\fR, or \fBcvs +checkout\fR overrides this default. +.SP +.IP "" 0 +\fB-l[\fIrev\fB]\fR +.IP "" 2 +Lock the revision with number \fIrev\fR. If a branch +is given, lock the latest revision on that branch. If +\fIrev\fR is omitted, lock the latest revision on the +default branch. There can be no space between +\fB-l\fR and its argument. +.SP +This can be used in conjunction with the +\fBrcslock.pl\fR script in the \fBcontrib\fR +directory of the \fBcvs\fR source distribution to +provide reserved checkouts (where only one user can be +editing a given file at a time). See the comments in +that file for details (and see the \fBREADME\fR file +in that directory for disclaimers about the unsupported +nature of contrib). According to comments in that +file, locking must set to strict (which is the default). +.SP +.IP "" 0 +\fB-L\fR +.IP "" 2 +Set locking to strict. Strict locking means that the +owner of an RCS file is not exempt from locking for +checkin. For use with \fBcvs\fR, strict locking must be +set; see the discussion under the \fB-l\fR option above. +.SP +.IX "Changing a log message" +.IX "Replacing a log message" +.IX "Correcting a log message" +.IX "Fixing a log message" +.IX "Log message, correcting" +.IP "" 0 +\fB-m\fIrev\fB:\fImsg\fB\fR +.IP "" 2 +Replace the log message of revision \fIrev\fR with +\fImsg\fR. +.SP +.IP "" 0 +\fB-N\fIname\fB[:[\fIrev\fB]]\fR +.IP "" 2 +Act like \fB-n\fR, except override any previous +assignment of \fIname\fR. For use with magic branches, +see see node `Magic branch numbers\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-n\fIname\fB[:[\fIrev\fB]]\fR +.IP "" 2 +Associate the symbolic name \fIname\fR with the branch +or revision \fIrev\fR. It is normally better to use +\fBcvs tag\fR or \fBcvs rtag\fR instead. Delete the +symbolic name if both \fB:\fR and \fIrev\fR are +omitted; otherwise, print an error message if +\fIname\fR is already associated with another number. +If \fIrev\fR is symbolic, it is expanded before +association. A \fIrev\fR consisting of a branch number +followed by a \fB.\fR stands for the current latest +revision in the branch. A \fB:\fR with an empty +\fIrev\fR stands for the current latest revision on the +default branch, normally the trunk. For example, +\fBcvs admin -n\fIname\fB:\fR associates \fIname\fR with the +current latest revision of all the RCS files; +this contrasts with \fBcvs admin -n\fIname\fB:$\fR which +associates \fIname\fR with the revision numbers +extracted from keyword strings in the corresponding +working files. +.SP +.IX "Deleting revisions" +.IX "Outdating revisions" +.IX "Saving space" +.IP "" 0 +\fB-o\fIrange\fB\fR +.IP "" 2 +Deletes (\fIoutdates\fR) the revisions given by +\fIrange\fR. +.SP +Note that this command can be quite dangerous unless +you know \fIexactly\fR what you are doing (for example +see the warnings below about how the +\fIrev1\fR:\fIrev2\fR syntax is confusing). +.SP +If you are short on disc this option might help you. +But think twice before using it\(emthere is no way short +of restoring the latest backup to undo this command! +If you delete different revisions than you planned, +either due to carelessness or (heaven forbid) a \fBcvs\fR +bug, there is no opportunity to correct the error +before the revisions are deleted. It probably would be +a good idea to experiment on a copy of the repository +first. +.SP +Specify \fIrange\fR in one of the following ways: +.SP +.IP "" 2 +\fB\fIrev1\fB::\fIrev2\fB\fR +.IP "" 4 +Collapse all revisions between rev1 and rev2, so that +\fBcvs\fR only stores the differences associated with going +from rev1 to rev2, not intermediate steps. For +example, after \fB-o 1.3::1.5\fR one can retrieve +revision 1.3, revision 1.5, or the differences to get +from 1.3 to 1.5, but not the revision 1.4, or the +differences between 1.3 and 1.4. Other examples: +\fB-o 1.3::1.4\fR and \fB-o 1.3::1.3\fR have no +effect, because there are no intermediate revisions to +remove. +.SP +.IP "" 2 +\fB::\fIrev\fB\fR +.IP "" 4 +Collapse revisions between the beginning of the branch +containing \fIrev\fR and \fIrev\fR itself. The +branchpoint and \fIrev\fR are left intact. For +example, \fB-o ::1.3.2.6\fR deletes revision 1.3.2.1, +revision 1.3.2.5, and everything in between, but leaves +1.3 and 1.3.2.6 intact. +.SP +.IP "" 2 +\fB\fIrev\fB::\fR +.IP "" 4 +Collapse revisions between \fIrev\fR and the end of the +branch containing \fIrev\fR. Revision \fIrev\fR is +left intact but the head revision is deleted. +.SP +.IP "" 2 +\fB\fIrev\fB\fR +.IP "" 4 +Delete the revision \fIrev\fR. For example, \fB-o +1.3\fR is equivalent to \fB-o 1.2::1.4\fR. +.SP +.IP "" 2 +\fB\fIrev1\fB:\fIrev2\fB\fR +.IP "" 4 +Delete the revisions from \fIrev1\fR to \fIrev2\fR, +inclusive, on the same branch. One will not be able to +retrieve \fIrev1\fR or \fIrev2\fR or any of the +revisions in between. For example, the command +\fBcvs admin -oR_1_01:R_1_02 \&.\fR is rarely useful. +It means to delete revisions up to, and including, the +tag R_1_02. But beware! If there are files that have not +changed between R_1_02 and R_1_03 the file will have +\fIthe same\fR numerical revision number assigned to +the tags R_1_02 and R_1_03. So not only will it be +impossible to retrieve R_1_02; R_1_03 will also have to +be restored from the tapes! In most cases you want to +specify \fIrev1\fR::\fIrev2\fR instead. +.SP +.IP "" 2 +\fB:\fIrev\fB\fR +.IP "" 4 +Delete revisions from the beginning of the +branch containing \fIrev\fR up to and including +\fIrev\fR. +.SP +.IP "" 2 +\fB\fIrev\fB:\fR +.IP "" 4 +Delete revisions from revision \fIrev\fR, including +\fIrev\fR itself, to the end of the branch containing +\fIrev\fR. +.SP +None of the revisions to be deleted may have +branches or locks. +.SP +If any of the revisions to be deleted have symbolic +names, and one specifies one of the \fB::\fR syntaxes, +then \fBcvs\fR will give an error and not delete any +revisions. If you really want to delete both the +symbolic names and the revisions, first delete the +symbolic names with \fBcvs tag -d\fR, then run +\fBcvs admin -o\fR. If one specifies the +non-\fB::\fR syntaxes, then \fBcvs\fR will delete the +revisions but leave the symbolic names pointing to +nonexistent revisions. This behavior is preserved for +compatibility with previous versions of \fBcvs\fR, but +because it isn\(aqt very useful, in the future it may +change to be like the \fB::\fR case. +.SP +Due to the way \fBcvs\fR handles branches \fIrev\fR +cannot be specified symbolically if it is a branch. +see node `Magic branch numbers\(aq in the CVS manual, for an explanation. +.SP +Make sure that no-one has checked out a copy of the +revision you outdate. Strange things will happen if he +starts to edit it and tries to check it back in. For +this reason, this option is not a good way to take back +a bogus commit; commit a new revision undoing the bogus +change instead (see node `Merging two revisions\(aq in the CVS manual). +.SP +.IP "" 0 +\fB-q\fR +.IP "" 2 +Run quietly; do not print diagnostics. +.SP +.IP "" 0 +\fB-s\fIstate\fB[:\fIrev\fB]\fR +.IP "" 2 +Useful with \fBcvs\fR. Set the state attribute of the +revision \fIrev\fR to \fIstate\fR. If \fIrev\fR is a +branch number, assume the latest revision on that +branch. If \fIrev\fR is omitted, assume the latest +revision on the default branch. Any identifier is +acceptable for \fIstate\fR. A useful set of states is +\fBExp\fR (for experimental), \fBStab\fR (for +stable), and \fBRel\fR (for released). By default, +the state of a new revision is set to \fBExp\fR when +it is created. The state is visible in the output from +\fIcvs log\fR (see node `log\(aq in the CVS manual), and in the +\fB$\fP\fPLog$\fR and \fB$\fP\fPState$\fR keywords +(see node `Keyword substitution\(aq in the CVS manual). Note that \fBcvs\fR +uses the \fBdead\fR state for its own purposes; to +take a file to or from the \fBdead\fR state use +commands like \fBcvs remove\fR and \fBcvs add\fR, not +\fBcvs admin -s\fR. +.SP +.IP "" 0 +\fB-t[\fIfile\fB]\fR +.IP "" 2 +Useful with \fBcvs\fR. Write descriptive text from the +contents of the named \fIfile\fR into the RCS file, +deleting the existing text. The \fIfile\fR pathname +may not begin with \fB-\fR. The descriptive text can be seen in the +output from \fBcvs log\fR (see node `log\(aq in the CVS manual). +There can be no space between \fB-t\fR and its argument. +.SP +If \fIfile\fR is omitted, +obtain the text from standard input, terminated by +end-of-file or by a line containing \fB.\fR by itself. +Prompt for the text if interaction is possible; see +\fB-I\fR. +.SP +.IP "" 0 +\fB-t-\fIstring\fB\fR +.IP "" 2 +Similar to \fB-t\fIfile\fB\fR. Write descriptive text +from the \fIstring\fR into the \fBrcs\fR file, deleting +the existing text. +There can be no space between \fB-t\fR and its argument. +.SP +.IP "" 0 +\fB-U\fR +.IP "" 2 +Set locking to non-strict. Non-strict locking means +that the owner of a file need not lock a revision for +checkin. For use with \fBcvs\fR, strict locking must be +set; see the discussion under the \fB-l\fR option +above. +.SP +.IP "" 0 +\fB-u[\fIrev\fB]\fR +.IP "" 2 +See the option \fB-l\fR above, for a discussion of +using this option with \fBcvs\fR. Unlock the revision +with number \fIrev\fR. If a branch is given, unlock +the latest revision on that branch. If \fIrev\fR is +omitted, remove the latest lock held by the caller. +Normally, only the locker of a revision may unlock it; +somebody else unlocking a revision breaks the lock. +This causes the original locker to be sent a \fBcommit\fR +notification (see node `Getting Notified\(aq in the CVS manual). +There can be no space between \fB-u\fR and its argument. +.SP +.IP "" 0 +\fB-V\fIn\fB\fR +.IP "" 2 +In previous versions of \fBcvs\fR, this option meant to +write an \fBrcs\fR file which would be acceptable to +\fBrcs\fR version \fIn\fR, but it is now obsolete and +specifying it will produce an error. +.SP +.IP "" 0 +\fB-x\fIsuffixes\fB\fR +.IP "" 2 +In previous versions of \fBcvs\fR, this was documented +as a way of specifying the names of the \fBrcs\fR +files. However, \fBcvs\fR has always required that the +\fBrcs\fR files used by \fBcvs\fR end in \fB,v\fR, so +this option has never done anything useful. +.SP +.SP +.SH "annotate" +.SS "What revision modified each line of a file?" +.IX "annotate (subcommand)" +.SP +.IP "\(bu" 2 +Synopsis: annotate [options] files\&... +.IP "\(bu" 2 +Requires: repository. +.IP "\(bu" 2 +Changes: nothing. +.SP +For each file in \fIfiles\fR, print the head revision +of the trunk, together with information on the last +modification for each line. +.SP +.SH "annotate options" +.SP +These standard options are supported by \fBannotate\fR +(see node `Common options\(aq in the CVS manual, for a complete description of +them): +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local directory only, no recursion. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Process directories recursively. +.SP +.IP "" 0 +\fB-f\fR +.IP "" 2 +Use head revision if tag/date not found. +.SP +.IP "" 0 +\fB-F\fR +.IP "" 2 +Annotate binary files. +.SP +.IP "" 0 +\fB-r \fIrevision\fB\fR +.IP "" 2 +Annotate file as of specified revision/tag. +.SP +.IP "" 0 +\fB-D \fIdate\fB\fR +.IP "" 2 +Annotate file as of specified date. +.SP +.SH "annotate example" +.SP +For example: +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs annotate ssfile +.IP "" 2 +Annotations for ssfile +.IP "" 2 +*************** +.IP "" 2 +1.1 (mary 27-Mar-96): ssfile line 1 +.IP "" 2 +1.2 (joe 28-Mar-96): ssfile line 2 + +.PD +.IP "" 0 +.SP +The file \fBssfile\fR currently contains two lines. +The \fBssfile line 1\fR line was checked in by +\fBmary\fR on March 27. Then, on March 28, \fBjoe\fR +added a line \fBssfile line 2\fR, without modifying +the \fBssfile line 1\fR line. This report doesn\(aqt +tell you anything about lines which have been deleted +or replaced; you need to use \fBcvs diff\fR for that +(see node `diff\(aq in the CVS manual). +.SP +The options to \fBcvs annotate\fR are listed in +see node `Invoking CVS\(aq in the CVS manual, and can be used to select the files +and revisions to annotate. The options are described +in more detail there and in see node `Common options\(aq in the CVS manual. +.SP +.SH "checkout" +.SS "Check out sources for editing" +.IX "checkout (subcommand)" +.IX "co (subcommand)" +.SP +.IP "\(bu" 2 +Synopsis: checkout [options] modules\&... +.IP "\(bu" 2 +Requires: repository. +.IP "\(bu" 2 +Changes: working directory. +.IP "\(bu" 2 +Synonyms: co, get +.SP +Create or update a working directory containing copies of the +source files specified by \fImodules\fR. You must execute +\fBcheckout\fR before using most of the other \fBcvs\fR +commands, since most of them operate on your working +directory. +.SP +The \fImodules\fR are either +symbolic names for some +collection of source directories and files, or paths to +directories or files in the repository. The symbolic +names are defined in the \fBmodules\fR file. +see node `modules\(aq in the CVS manual. +.SP +Depending on the modules you specify, \fBcheckout\fR may +recursively create directories and populate them with +the appropriate source files. You can then edit these +source files at any time (regardless of whether other +software developers are editing their own copies of the +sources); update them to include new changes applied by +others to the source repository; or commit your work as +a permanent change to the source repository. +.SP +Note that \fBcheckout\fR is used to create +directories. The top-level directory created is always +added to the directory where \fBcheckout\fR is +invoked, and usually has the same name as the specified +module. In the case of a module alias, the created +sub-directory may have a different name, but you can be +sure that it will be a sub-directory, and that +\fBcheckout\fR will show the relative path leading to +each file as it is extracted into your private work +area (unless you specify the \fB-Q\fR global option). +.SP +The files created by \fBcheckout\fR are created +read-write, unless the \fB-r\fR option to \fBcvs\fR +(see node `Global options\(aq in the CVS manual) is specified, the +\fBCVSREAD\fR environment variable is specified +(see node `Environment variables\(aq in the CVS manual), or a watch is in +effect for that file (see node `Watches\(aq in the CVS manual). +.SP +Note that running \fBcheckout\fR on a directory that was already +built by a prior \fBcheckout\fR is also permitted. +This is similar to specifying the \fB-d\fR option +to the \fBupdate\fR command in the sense that new +directories that have been created in the repository +will appear in your work area. +However, \fBcheckout\fR takes a module name whereas +\fBupdate\fR takes a directory name. Also +to use \fBcheckout\fR this way it must be run from the +top level directory (where you originally ran +\fBcheckout\fR from), so before you run +\fBcheckout\fR to update an existing directory, don\(aqt +forget to change your directory to the top level +directory. +.SP +For the output produced by the \fBcheckout\fR command +see see node `update output\(aq in the CVS manual. +.SP +.SH "checkout options" +.SP +These standard options are supported by \fBcheckout\fR +(see node `Common options\(aq in the CVS manual, for a complete description of +them): +.SP +.IP "" 0 +\fB-D \fIdate\fB\fR +.IP "" 2 +Use the most recent revision no later than \fIdate\fR. +This option is sticky, and implies \fB-P\fR. See +see node `Sticky tags\(aq in the CVS manual, for more information on sticky tags/dates. +.SP +.IP "" 0 +\fB-f\fR +.IP "" 2 +Only useful with the \fB-D \fIdate\fB\fR or \fB-r +\fItag\fB\fR flags. If no matching revision is found, +retrieve the most recent revision (instead of ignoring +the file). +.SP +.IP "" 0 +\fB-k \fIkflag\fB\fR +.IP "" 2 +Process keywords according to \fIkflag\fR. See +see node `Keyword substitution\(aq in the CVS manual. +This option is sticky; future updates of +this file in this working directory will use the same +\fIkflag\fR. The \fBstatus\fR command can be viewed +to see the sticky options. See see node `Invoking CVS\(aq in the CVS manual, for +more information on the \fBstatus\fR command. +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local; run only in current working directory. +.SP +.IP "" 0 +\fB-n\fR +.IP "" 2 +Do not run any checkout program (as specified +with the \fB-o\fR option in the modules file; +see node `modules\(aq in the CVS manual). +.SP +.IP "" 0 +\fB-P\fR +.IP "" 2 +Prune empty directories. See see node `Moving directories\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-p\fR +.IP "" 2 +Pipe files to the standard output. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Checkout directories recursively. This option is on by default. +.SP +.IP "" 0 +\fB-r \fItag\fB\fR +.IP "" 2 +Use revision \fItag\fR. This option is sticky, and implies \fB-P\fR. +See see node `Sticky tags\(aq in the CVS manual, for more information on sticky tags/dates. +.SP +In addition to those, you can use these special command +options with \fBcheckout\fR: +.SP +.IP "" 0 +\fB-A\fR +.IP "" 2 +Reset any sticky tags, dates, or \fB-k\fR options. +See see node `Sticky tags\(aq in the CVS manual, for more information on sticky tags/dates. +.SP +.IP "" 0 +\fB-c\fR +.IP "" 2 +Copy the module file, sorted, to the standard output, +instead of creating or modifying any files or +directories in your working directory. +.SP +.IP "" 0 +\fB-d \fIdir\fB\fR +.IP "" 2 +Create a directory called \fIdir\fR for the working +files, instead of using the module name. In general, +using this flag is equivalent to using \fBmkdir +\fIdir\fB; cd \fIdir\fB\fR followed by the checkout +command without the \fB-d\fR flag. +.SP +There is an important exception, however. It is very +convenient when checking out a single item to have the +output appear in a directory that doesn\(aqt contain empty +intermediate directories. In this case \fIonly\fR, +\fBcvs\fR tries to \`\`shorten\(aq\(aq pathnames to avoid those empty +directories. +.SP +For example, given a module \fBfoo\fR that contains +the file \fBbar.c\fR, the command \fBcvs co -d dir +foo\fR will create directory \fBdir\fR and place +\fBbar.c\fR inside. Similarly, given a module +\fBbar\fR which has subdirectory \fBbaz\fR wherein +there is a file \fBquux.c\fR, the command \fBcvs co +-d dir bar/baz\fR will create directory \fBdir\fR and +place \fBquux.c\fR inside. +.SP +Using the \fB-N\fR flag will defeat this behavior. +Given the same module definitions above, \fBcvs co +-N -d dir foo\fR will create directories \fBdir/foo\fR +and place \fBbar.c\fR inside, while \fBcvs co -N -d +dir bar/baz\fR will create directories \fBdir/bar/baz\fR +and place \fBquux.c\fR inside. +.SP +.IP "" 0 +\fB-j \fItag\fB\fR +.IP "" 2 +With two \fB-j\fR options, merge changes from the +revision specified with the first \fB-j\fR option to +the revision specified with the second \fBj\fR option, +into the working directory. +.SP +With one \fB-j\fR option, merge changes from the +ancestor revision to the revision specified with the +\fB-j\fR option, into the working directory. The +ancestor revision is the common ancestor of the +revision which the working directory is based on, and +the revision specified in the \fB-j\fR option. +.SP +In addition, each -j option can contain an optional +date specification which, when used with branches, can +limit the chosen revision to one within a specific +date. An optional date is specified by adding a colon +(:) to the tag: +\fB-j\fISymbolic_Tag\fB:\fIDate_Specifier\fB\fR. +.SP +see node `Branching and merging\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-N\fR +.IP "" 2 +Only useful together with \fB-d \fIdir\fB\fR. With +this option, \fBcvs\fR will not \`\`shorten\(aq\(aq module paths +in your working directory when you check out a single +module. See the \fB-d\fR flag for examples and a +discussion. +.SP +.IP "" 0 +\fB-s\fR +.IP "" 2 +Like \fB-c\fR, but include the status of all modules, +and sort it by the status string. see node `modules\(aq in the CVS manual, for +info about the \fB-s\fR option that is used inside the +modules file to set the module status. +.SP +.SH "checkout examples" +.SP +Get a copy of the module \fBtc\fR: +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs checkout tc + +.PD +.IP "" 0 +.SP +Get a copy of the module \fBtc\fR as it looked one day +ago: +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs checkout -D yesterday tc + +.PD +.IP "" 0 +.SP +.SH "commit" +.SS "Check files into the repository" +.IX "commit (subcommand)" +.SP +.IP "\(bu" 2 +Synopsis: commit [-lnRf] [-m \(aqlog_message\(aq | +-F file] [-r revision] [files\&...] +.IP "\(bu" 2 +Requires: working directory, repository. +.IP "\(bu" 2 +Changes: repository. +.IP "\(bu" 2 +Synonym: ci +.SP +Use \fBcommit\fR when you want to incorporate changes +from your working source files into the source +repository. +.SP +If you don\(aqt specify particular files to commit, all of +the files in your working current directory are +examined. \fBcommit\fR is careful to change in the +repository only those files that you have really +changed. By default (or if you explicitly specify the +\fB-R\fR option), files in subdirectories are also +examined and committed if they have changed; you can +use the \fB-l\fR option to limit \fBcommit\fR to the +current directory only. +.SP +\fBcommit\fR verifies that the selected files are up +to date with the current revisions in the source +repository; it will notify you, and exit without +committing, if any of the specified files must be made +current first with \fBupdate\fR (see node `update\(aq in the CVS manual). +\fBcommit\fR does not call the \fBupdate\fR command +for you, but rather leaves that for you to do when the +time is right. +.SP +When all is well, an editor is invoked to allow you to +enter a log message that will be written to one or more +logging programs (see node `modules\(aq in the CVS manual, and see node `loginfo\(aq in the CVS manual) +and placed in the \fBrcs\fR file inside the +repository. This log message can be retrieved with the +\fBlog\fR command; see see node `log\(aq in the CVS manual. You can specify the +log message on the command line with the \fB-m +\fImessage\fB\fR option, and thus avoid the editor invocation, +or use the \fB-F \fIfile\fB\fR option to specify +that the argument file contains the log message. +.SP +.SH "commit options" +.SP +These standard options are supported by \fBcommit\fR +(see node `Common options\(aq in the CVS manual, for a complete description of +them): +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local; run only in current working directory. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Commit directories recursively. This is on by default. +.SP +.IP "" 0 +\fB-r \fIrevision\fB\fR +.IP "" 2 +Commit to \fIrevision\fR. \fIrevision\fR must be +either a branch, or a revision on the main trunk that +is higher than any existing revision number +(see node `Assigning revisions\(aq in the CVS manual). You +cannot commit to a specific revision on a branch. +.SP +\fBcommit\fR also supports these options: +.SP +.IP "" 0 +\fB-F \fIfile\fB\fR +.IP "" 2 +Read the log message from \fIfile\fR, instead +of invoking an editor. +.SP +.IP "" 0 +\fB-f\fR +.IP "" 2 +Note that this is not the standard behavior of +the \fB-f\fR option as defined in see node `Common options\(aq in the CVS manual. +.SP +Force \fBcvs\fR to commit a new revision even if you haven\(aqt +made any changes to the file. If the current revision +of \fIfile\fR is 1.7, then the following two commands +are equivalent: +.SP +.PD 0 +.SP +.IP "" 4 +$ cvs commit -f \fIfile\fR +.IP "" 4 +$ cvs commit -r 1.8 \fIfile\fR + +.PD +.IP "" 2 +.SP +The \fB-f\fR option disables recursion (i.e., it +implies \fB-l\fR). To force \fBcvs\fR to commit a new +revision for all files in all subdirectories, you must +use \fB-f -R\fR. +.SP +.IP "" 0 +\fB-m \fImessage\fB\fR +.IP "" 2 +Use \fImessage\fR as the log message, instead of +invoking an editor. +.SP +.SH "commit examples" +.SP +.SS "Committing to a branch" +.SP +You can commit to a branch revision (one that has an +even number of dots) with the \fB-r\fR option. To +create a branch revision, use the \fB-b\fR option +of the \fBrtag\fR or \fBtag\fR commands +(see node `Branching and merging\(aq in the CVS manual). Then, either \fBcheckout\fR or +\fBupdate\fR can be used to base your sources on the +newly created branch. From that point on, all +\fBcommit\fR changes made within these working sources +will be automatically added to a branch revision, +thereby not disturbing main-line development in any +way. For example, if you had to create a patch to the +1.2 version of the product, even though the 2.0 version +is already under development, you might do: +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs rtag -b -r FCS1_2 FCS1_2_Patch product_module +.IP "" 2 +$ cvs checkout -r FCS1_2_Patch product_module +.IP "" 2 +$ cd product_module +.IP "" 2 +[[ hack away ]] +.IP "" 2 +$ cvs commit + +.PD +.IP "" 0 +.SP +This works automatically since the \fB-r\fR option is +sticky. +.SP +.SS "Creating the branch after editing" +.SP +Say you have been working on some extremely +experimental software, based on whatever revision you +happened to checkout last week. If others in your +group would like to work on this software with you, but +without disturbing main-line development, you could +commit your change to a new branch. Others can then +checkout your experimental stuff and utilize the full +benefit of \fBcvs\fR conflict resolution. The scenario might +look like: +.SP +.PD 0 +.SP +.IP "" 2 +[[ hacked sources are present ]] +.IP "" 2 +$ cvs tag -b EXPR1 +.IP "" 2 +$ cvs update -r EXPR1 +.IP "" 2 +$ cvs commit + +.PD +.IP "" 0 +.SP +The \fBupdate\fR command will make the \fB-r +EXPR1\fR option sticky on all files. Note that your +changes to the files will never be removed by the +\fBupdate\fR command. The \fBcommit\fR will +automatically commit to the correct branch, because the +\fB-r\fR is sticky. You could also do like this: +.SP +.PD 0 +.SP +.IP "" 2 +[[ hacked sources are present ]] +.IP "" 2 +$ cvs tag -b EXPR1 +.IP "" 2 +$ cvs commit -r EXPR1 + +.PD +.IP "" 0 +.SP +but then, only those files that were changed by you +will have the \fB-r EXPR1\fR sticky flag. If you hack +away, and commit without specifying the \fB-r EXPR1\fR +flag, some files may accidentally end up on the main +trunk. +.SP +To work with you on the experimental change, others +would simply do +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs checkout -r EXPR1 whatever_module + +.PD +.IP "" 0 +.SP +.SH "diff" +.SS "Show differences between revisions" +.IX "diff (subcommand)" +.SP +.IP "\(bu" 2 +Synopsis: diff [-lR] [-k kflag] [format_options] [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files\&...] +.IP "\(bu" 2 +Requires: working directory, repository. +.IP "\(bu" 2 +Changes: nothing. +.SP +The \fBdiff\fR command is used to compare different +revisions of files. The default action is to compare +your working files with the revisions they were based +on, and report any differences that are found. +.SP +If any file names are given, only those files are +compared. If any directories are given, all files +under them will be compared. +.SP +The exit status for diff is different than for other +\fBcvs\fR commands; for details see node `Exit status\(aq in the CVS manual. +.SP +.SH "diff options" +.SP +These standard options are supported by \fBdiff\fR +(see node `Common options\(aq in the CVS manual, for a complete description of +them): +.SP +.IP "" 0 +\fB-D \fIdate\fB\fR +.IP "" 2 +Use the most recent revision no later than \fIdate\fR. +See \fB-r\fR for how this affects the comparison. +.SP +.IP "" 0 +\fB-k \fIkflag\fB\fR +.IP "" 2 +Process keywords according to \fIkflag\fR. See +see node `Keyword substitution\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local; run only in current working directory. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Examine directories recursively. This option is on by +default. +.SP +.IP "" 0 +\fB-r \fItag\fB\fR +.IP "" 2 +Compare with revision \fItag\fR. Zero, one or two +\fB-r\fR options can be present. With no \fB-r\fR +option, the working file will be compared with the +revision it was based on. With one \fB-r\fR, that +revision will be compared to your current working file. +With two \fB-r\fR options those two revisions will be +compared (and your working file will not affect the +outcome in any way). +.SP +One or both \fB-r\fR options can be replaced by a +\fB-D \fIdate\fB\fR option, described above. +.SP +The following options specify the format of the +output. They have the same meaning as in GNU diff. +Most options have two equivalent names, one of which is a single letter +preceded by \fB-\fR, and the other of which is a long name preceded by +\fB--\fR. +.SP +.IP "" 0 +\fB-\fIlines\fB\fR +.IP "" 2 +Show \fIlines\fR (an integer) lines of context. This option does not +specify an output format by itself; it has no effect unless it is +combined with \fB-c\fR or \fB-u\fR. This option is obsolete. For proper +operation, \fBpatch\fR typically needs at least two lines of context. +.SP +.IP "" 0 +\fB-a\fR +.IP "" 2 +Treat all files as text and compare them line-by-line, even if they +do not seem to be text. +.SP +.IP "" 0 +\fB-b\fR +.IP "" 2 +Ignore trailing white space and consider all other sequences of one or +more white space characters to be equivalent. +.SP +.IP "" 0 +\fB-B\fR +.IP "" 2 +Ignore changes that just insert or delete blank lines. +.SP +.IP "" 0 +\fB--binary\fR +.IP "" 2 +Read and write data in binary mode. +.SP +.IP "" 0 +\fB--brief\fR +.IP "" 2 +Report only whether the files differ, not the details of the +differences. +.SP +.IP "" 0 +\fB-c\fR +.IP "" 2 +Use the context output format. +.SP +.IP "" 0 +\fB-C \fIlines\fB\fR +.IP "" 2 +.IP "" 0 +\fB--context\fR[\fB=\fIlines\fB\fR]\fB\fR +.IP "" 2 +Use the context output format, showing \fIlines\fR (an integer) lines of +context, or three if \fIlines\fR is not given. +For proper operation, \fBpatch\fR typically needs at least two lines of +context. +.SP +.IP "" 0 +\fB--changed-group-format=\fIformat\fB\fR +.IP "" 2 +Use \fIformat\fR to output a line group containing differing lines from +both files in if-then-else format. see node `Line group formats\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-d\fR +.IP "" 2 +Change the algorithm to perhaps find a smaller set of changes. This makes +\fBdiff\fR slower (sometimes much slower). +.SP +.IP "" 0 +\fB-e\fR +.IP "" 2 +.IP "" 0 +\fB--ed\fR +.IP "" 2 +Make output that is a valid \fBed\fR script. +.SP +.IP "" 0 +\fB--expand-tabs\fR +.IP "" 2 +Expand tabs to spaces in the output, to preserve the alignment of tabs +in the input files. +.SP +.IP "" 0 +\fB-f\fR +.IP "" 2 +Make output that looks vaguely like an \fBed\fR script but has changes +in the order they appear in the file. +.SP +.IP "" 0 +\fB-F \fIregexp\fB\fR +.IP "" 2 +In context and unified format, for each hunk of differences, show some +of the last preceding line that matches \fIregexp\fR. +.SP +.IP "" 0 +\fB--forward-ed\fR +.IP "" 2 +Make output that looks vaguely like an \fBed\fR script but has changes +in the order they appear in the file. +.SP +.IP "" 0 +\fB-H\fR +.IP "" 2 +Use heuristics to speed handling of large files that have numerous +scattered small changes. +.SP +.IP "" 0 +\fB--horizon-lines=\fIlines\fB\fR +.IP "" 2 +Do not discard the last \fIlines\fR lines of the common prefix +and the first \fIlines\fR lines of the common suffix. +.SP +.IP "" 0 +\fB-i\fR +.IP "" 2 +Ignore changes in case; consider upper- and lower-case letters +equivalent. +.SP +.IP "" 0 +\fB-I \fIregexp\fB\fR +.IP "" 2 +Ignore changes that just insert or delete lines that match \fIregexp\fR. +.SP +.IP "" 0 +\fB--ifdef=\fIname\fB\fR +.IP "" 2 +Make merged if-then-else output using \fIname\fR. +.SP +.IP "" 0 +\fB--ignore-all-space\fR +.IP "" 2 +Ignore white space when comparing lines. +.SP +.IP "" 0 +\fB--ignore-blank-lines\fR +.IP "" 2 +Ignore changes that just insert or delete blank lines. +.SP +.IP "" 0 +\fB--ignore-case\fR +.IP "" 2 +Ignore changes in case; consider upper- and lower-case to be the same. +.SP +.IP "" 0 +\fB--ignore-matching-lines=\fIregexp\fB\fR +.IP "" 2 +Ignore changes that just insert or delete lines that match \fIregexp\fR. +.SP +.IP "" 0 +\fB--ignore-space-change\fR +.IP "" 2 +Ignore trailing white space and consider all other sequences of one or +more white space characters to be equivalent. +.SP +.IP "" 0 +\fB--initial-tab\fR +.IP "" 2 +Output a tab rather than a space before the text of a line in normal or +context format. This causes the alignment of tabs in the line to look +normal. +.SP +.IP "" 0 +\fB-L \fIlabel\fB\fR +.IP "" 2 +Use \fIlabel\fR instead of the file name in the context format +and unified format headers. +.SP +.IP "" 0 +\fB--label=\fIlabel\fB\fR +.IP "" 2 +Use \fIlabel\fR instead of the file name in the context format +and unified format headers. +.SP +.IP "" 0 +\fB--left-column\fR +.IP "" 2 +Print only the left column of two common lines in side by side format. +.SP +.IP "" 0 +\fB--line-format=\fIformat\fB\fR +.IP "" 2 +Use \fIformat\fR to output all input lines in if-then-else format. +see node `Line formats\(aq in the CVS manual. +.SP +.IP "" 0 +\fB--minimal\fR +.IP "" 2 +Change the algorithm to perhaps find a smaller set of changes. This +makes \fBdiff\fR slower (sometimes much slower). +.SP +.IP "" 0 +\fB-n\fR +.IP "" 2 +Output RCS-format diffs; like \fB-f\fR except that each command +specifies the number of lines affected. +.SP +.IP "" 0 +\fB-N\fR +.IP "" 2 +.IP "" 0 +\fB--new-file\fR +.IP "" 2 +In directory comparison, if a file is found in only one directory, +treat it as present but empty in the other directory. +.SP +.IP "" 0 +\fB--new-group-format=\fIformat\fB\fR +.IP "" 2 +Use \fIformat\fR to output a group of lines taken from just the second +file in if-then-else format. see node `Line group formats\(aq in the CVS manual. +.SP +.IP "" 0 +\fB--new-line-format=\fIformat\fB\fR +.IP "" 2 +Use \fIformat\fR to output a line taken from just the second file in +if-then-else format. see node `Line formats\(aq in the CVS manual. +.SP +.IP "" 0 +\fB--old-group-format=\fIformat\fB\fR +.IP "" 2 +Use \fIformat\fR to output a group of lines taken from just the first +file in if-then-else format. see node `Line group formats\(aq in the CVS manual. +.SP +.IP "" 0 +\fB--old-line-format=\fIformat\fB\fR +.IP "" 2 +Use \fIformat\fR to output a line taken from just the first file in +if-then-else format. see node `Line formats\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-p\fR +.IP "" 2 +Show which C function each change is in. +.SP +.IP "" 0 +\fB--rcs\fR +.IP "" 2 +Output RCS-format diffs; like \fB-f\fR except that each command +specifies the number of lines affected. +.SP +.IP "" 0 +\fB--report-identical-files\fR +.IP "" 2 +.IP "" 0 +\fB-s\fR +.IP "" 2 +Report when two files are the same. +.SP +.IP "" 0 +\fB--show-c-function\fR +.IP "" 2 +Show which C function each change is in. +.SP +.IP "" 0 +\fB--show-function-line=\fIregexp\fB\fR +.IP "" 2 +In context and unified format, for each hunk of differences, show some +of the last preceding line that matches \fIregexp\fR. +.SP +.IP "" 0 +\fB--side-by-side\fR +.IP "" 2 +Use the side by side output format. +.SP +.IP "" 0 +\fB--speed-large-files\fR +.IP "" 2 +Use heuristics to speed handling of large files that have numerous +scattered small changes. +.SP +.IP "" 0 +\fB--suppress-common-lines\fR +.IP "" 2 +Do not print common lines in side by side format. +.SP +.IP "" 0 +\fB-t\fR +.IP "" 2 +Expand tabs to spaces in the output, to preserve the alignment of tabs +in the input files. +.SP +.IP "" 0 +\fB-T\fR +.IP "" 2 +Output a tab rather than a space before the text of a line in normal or +context format. This causes the alignment of tabs in the line to look +normal. +.SP +.IP "" 0 +\fB--text\fR +.IP "" 2 +Treat all files as text and compare them line-by-line, even if they +do not appear to be text. +.SP +.IP "" 0 +\fB-u\fR +.IP "" 2 +Use the unified output format. +.SP +.IP "" 0 +\fB--unchanged-group-format=\fIformat\fB\fR +.IP "" 2 +Use \fIformat\fR to output a group of common lines taken from both files +in if-then-else format. see node `Line group formats\(aq in the CVS manual. +.SP +.IP "" 0 +\fB--unchanged-line-format=\fIformat\fB\fR +.IP "" 2 +Use \fIformat\fR to output a line common to both files in if-then-else +format. see node `Line formats\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-U \fIlines\fB\fR +.IP "" 2 +.IP "" 0 +\fB--unified\fR[\fB=\fIlines\fB\fR]\fB\fR +.IP "" 2 +Use the unified output format, showing \fIlines\fR (an integer) lines of +context, or three if \fIlines\fR is not given. +For proper operation, \fBpatch\fR typically needs at least two lines of +context. +.SP +.IP "" 0 +\fB-w\fR +.IP "" 2 +Ignore white space when comparing lines. +.SP +.IP "" 0 +\fB-W \fIcolumns\fB\fR +.IP "" 2 +.IP "" 0 +\fB--width=\fIcolumns\fB\fR +.IP "" 2 +Use an output width of \fIcolumns\fR in side by side format. +.SP +.IP "" 0 +\fB-y\fR +.IP "" 2 +Use the side by side output format. +.SP +.SH "Line group formats" +.SP +Line group formats let you specify formats suitable for many +applications that allow if-then-else input, including programming +languages and text formatting languages. A line group format specifies +the output format for a contiguous group of similar lines. +.SP +For example, the following command compares the TeX file \fBmyfile\fR +with the original version from the repository, +and outputs a merged file in which old regions are +surrounded by \fB\\begin{em}\fR-\fB\\end{em}\fR lines, and new +regions are surrounded by \fB\\begin{bf}\fR-\fB\\end{bf}\fR lines. +.SP +.PD 0 +.SP +.IP "" 2 +cvs diff \\ +.IP "" 2 + --old-group-format=\(aq\\begin{em} +.IP "" 2 +%<\\end{em} +.IP "" 2 +\(aq \\ +.IP "" 2 + --new-group-format=\(aq\\begin{bf} +.IP "" 2 +%>\\end{bf} +.IP "" 2 +\(aq \\ +.IP "" 2 + myfile + +.PD +.IP "" 0 +.SP +The following command is equivalent to the above example, but it is a +little more verbose, because it spells out the default line group formats. +.SP +.PD 0 +.SP +.IP "" 2 +cvs diff \\ +.IP "" 2 + --old-group-format=\(aq\\begin{em} +.IP "" 2 +%<\\end{em} +.IP "" 2 +\(aq \\ +.IP "" 2 + --new-group-format=\(aq\\begin{bf} +.IP "" 2 +%>\\end{bf} +.IP "" 2 +\(aq \\ +.IP "" 2 + --unchanged-group-format=\(aq%=\(aq \\ +.IP "" 2 + --changed-group-format=\(aq\\begin{em} +.IP "" 2 +%<\\end{em} +.IP "" 2 +\\begin{bf} +.IP "" 2 +%>\\end{bf} +.IP "" 2 +\(aq \\ +.IP "" 2 + myfile + +.PD +.IP "" 0 +.SP +Here is a more advanced example, which outputs a diff listing with +headers containing line numbers in a \`\`plain English\(aq\(aq style. +.SP +.PD 0 +.SP +.IP "" 2 +cvs diff \\ +.IP "" 2 + --unchanged-group-format=\(aq\(aq \\ +.IP "" 2 + --old-group-format=\(aq-------- %dn line%(n=1?:s) deleted at %df: +.IP "" 2 +%<\(aq \\ +.IP "" 2 + --new-group-format=\(aq-------- %dN line%(N=1?:s) added after %de: +.IP "" 2 +%>\(aq \\ +.IP "" 2 + --changed-group-format=\(aq-------- %dn line%(n=1?:s) changed at %df: +.IP "" 2 +%<-------- to: +.IP "" 2 +%>\(aq \\ +.IP "" 2 + myfile + +.PD +.IP "" 0 +.SP +To specify a line group format, use one of the options +listed below. You can specify up to four line group formats, one for +each kind of line group. You should quote \fIformat\fR, because it +typically contains shell metacharacters. +.SP +.IP "" 0 +\fB--old-group-format=\fIformat\fB\fR +.IP "" 2 +These line groups are hunks containing only lines from the first file. +The default old group format is the same as the changed group format if +it is specified; otherwise it is a format that outputs the line group as-is. +.SP +.IP "" 0 +\fB--new-group-format=\fIformat\fB\fR +.IP "" 2 +These line groups are hunks containing only lines from the second +file. The default new group format is same as the changed group +format if it is specified; otherwise it is a format that outputs the +line group as-is. +.SP +.IP "" 0 +\fB--changed-group-format=\fIformat\fB\fR +.IP "" 2 +These line groups are hunks containing lines from both files. The +default changed group format is the concatenation of the old and new +group formats. +.SP +.IP "" 0 +\fB--unchanged-group-format=\fIformat\fB\fR +.IP "" 2 +These line groups contain lines common to both files. The default +unchanged group format is a format that outputs the line group as-is. +.SP +In a line group format, ordinary characters represent themselves; +conversion specifications start with \fB%\fR and have one of the +following forms. +.SP +.IP "" 0 +\fB%<\fR +.IP "" 2 +stands for the lines from the first file, including the trailing newline. +Each line is formatted according to the old line format (see node `Line formats\(aq in the CVS manual). +.SP +.IP "" 0 +\fB%>\fR +.IP "" 2 +stands for the lines from the second file, including the trailing newline. +Each line is formatted according to the new line format. +.SP +.IP "" 0 +\fB%=\fR +.IP "" 2 +stands for the lines common to both files, including the trailing newline. +Each line is formatted according to the unchanged line format. +.SP +.IP "" 0 +\fB%%\fR +.IP "" 2 +stands for \fB%\fR. +.SP +.IP "" 0 +\fB%c\(aq\fIC\fB\(aq\fR +.IP "" 2 +where \fIC\fR is a single character, stands for \fIC\fR. +\fIC\fR may not be a backslash or an apostrophe. +For example, \fB%c\(aq:\(aq\fR stands for a colon, even inside +the then-part of an if-then-else format, which a colon would +normally terminate. +.SP +.IP "" 0 +\fB%c\(aq\\\fIO\fB\(aq\fR +.IP "" 2 +where \fIO\fR is a string of 1, 2, or 3 octal digits, +stands for the character with octal code \fIO\fR. +For example, \fB%c\(aq\\0\(aq\fR stands for a null character. +.SP +.IP "" 0 +\fB\fIF\fB\fIn\fB\fR +.IP "" 2 +where \fIF\fR is a \fBprintf\fR conversion specification and \fIn\fR is one +of the following letters, stands for \fIn\fR\(aqs value formatted with \fIF\fR. +.SP +.IP "" 2 +\fBe\fR +.IP "" 4 +The line number of the line just before the group in the old file. +.SP +.IP "" 2 +\fBf\fR +.IP "" 4 +The line number of the first line in the group in the old file; +equals \fIe\fR + 1. +.SP +.IP "" 2 +\fBl\fR +.IP "" 4 +The line number of the last line in the group in the old file. +.SP +.IP "" 2 +\fBm\fR +.IP "" 4 +The line number of the line just after the group in the old file; +equals \fIl\fR + 1. +.SP +.IP "" 2 +\fBn\fR +.IP "" 4 +The number of lines in the group in the old file; equals \fIl\fR - \fIf\fR + 1. +.SP +.IP "" 2 +\fBE, F, L, M, N\fR +.IP "" 4 +Likewise, for lines in the new file. +.SP +.SP +The \fBprintf\fR conversion specification can be \fB%d\fR, +\fB%o\fR, \fB%x\fR, or \fB%X\fR, specifying decimal, octal, +lower case hexadecimal, or upper case hexadecimal output +respectively. After the \fB%\fR the following options can appear in +sequence: a \fB-\fR specifying left-justification; an integer +specifying the minimum field width; and a period followed by an +optional integer specifying the minimum number of digits. +For example, \fB%5dN\fR prints the number of new lines in the group +in a field of width 5 characters, using the \fBprintf\fR format \fB"%5d"\fR. +.SP +.IP "" 0 +\fB(\fIA\fB=\fIB\fB?\fIT\fB:\fIE\fB)\fR +.IP "" 2 +If \fIA\fR equals \fIB\fR then \fIT\fR else \fIE\fR. +\fIA\fR and \fIB\fR are each either a decimal constant +or a single letter interpreted as above. +This format spec is equivalent to \fIT\fR if +\fIA\fR\(aqs value equals \fIB\fR\(aqs; otherwise it is equivalent to \fIE\fR. +.SP +For example, \fB%(N=0?no:%dN) line%(N=1?:s)\fR is equivalent to +\fBno lines\fR if \fIN\fR (the number of lines in the group in the +new file) is 0, to \fB1 line\fR if \fIN\fR is 1, and to \fB%dN lines\fR +otherwise. +.SP +.SH "Line formats" +.SP +Line formats control how each line taken from an input file is +output as part of a line group in if-then-else format. +.SP +For example, the following command outputs text with a one-column +change indicator to the left of the text. The first column of output +is \fB-\fR for deleted lines, \fB|\fR for added lines, and a space +for unchanged lines. The formats contain newline characters where +newlines are desired on output. +.SP +.PD 0 +.SP +.IP "" 2 +cvs diff \\ +.IP "" 2 + --old-line-format=\(aq-%l +.IP "" 2 +\(aq \\ +.IP "" 2 + --new-line-format=\(aq|%l +.IP "" 2 +\(aq \\ +.IP "" 2 + --unchanged-line-format=\(aq %l +.IP "" 2 +\(aq \\ +.IP "" 2 + myfile + +.PD +.IP "" 0 +.SP +To specify a line format, use one of the following options. You should +quote \fIformat\fR, since it often contains shell metacharacters. +.SP +.IP "" 0 +\fB--old-line-format=\fIformat\fB\fR +.IP "" 2 +formats lines just from the first file. +.SP +.IP "" 0 +\fB--new-line-format=\fIformat\fB\fR +.IP "" 2 +formats lines just from the second file. +.SP +.IP "" 0 +\fB--unchanged-line-format=\fIformat\fB\fR +.IP "" 2 +formats lines common to both files. +.SP +.IP "" 0 +\fB--line-format=\fIformat\fB\fR +.IP "" 2 +formats all lines; in effect, it sets all three above options simultaneously. +.SP +In a line format, ordinary characters represent themselves; +conversion specifications start with \fB%\fR and have one of the +following forms. +.SP +.IP "" 0 +\fB%l\fR +.IP "" 2 +stands for the contents of the line, not counting its trailing +newline (if any). This format ignores whether the line is incomplete. +.SP +.IP "" 0 +\fB%L\fR +.IP "" 2 +stands for the contents of the line, including its trailing newline +(if any). If a line is incomplete, this format preserves its +incompleteness. +.SP +.IP "" 0 +\fB%%\fR +.IP "" 2 +stands for \fB%\fR. +.SP +.IP "" 0 +\fB%c\(aq\fIC\fB\(aq\fR +.IP "" 2 +where \fIC\fR is a single character, stands for \fIC\fR. +\fIC\fR may not be a backslash or an apostrophe. +For example, \fB%c\(aq:\(aq\fR stands for a colon. +.SP +.IP "" 0 +\fB%c\(aq\\\fIO\fB\(aq\fR +.IP "" 2 +where \fIO\fR is a string of 1, 2, or 3 octal digits, +stands for the character with octal code \fIO\fR. +For example, \fB%c\(aq\\0\(aq\fR stands for a null character. +.SP +.IP "" 0 +\fB\fIF\fBn\fR +.IP "" 2 +where \fIF\fR is a \fBprintf\fR conversion specification, +stands for the line number formatted with \fIF\fR. +For example, \fB%.5dn\fR prints the line number using the +\fBprintf\fR format \fB"%.5d"\fR. see node `Line group formats\(aq in the CVS manual, for +more about printf conversion specifications. +.SP +.SP +The default line format is \fB%l\fR followed by a newline character. +.SP +If the input contains tab characters and it is important that they line +up on output, you should ensure that \fB%l\fR or \fB%L\fR in a line +format is just after a tab stop (e.g. by preceding \fB%l\fR or +\fB%L\fR with a tab character), or you should use the \fB-t\fR or +\fB--expand-tabs\fR option. +.SP +Taken together, the line and line group formats let you specify many +different formats. For example, the following command uses a format +similar to \fBdiff\fR\(aqs normal format. You can tailor this command +to get fine control over \fBdiff\fR\(aqs output. +.SP +.PD 0 +.SP +.IP "" 2 +cvs diff \\ +.IP "" 2 + --old-line-format=\(aq< %l +.IP "" 2 +\(aq \\ +.IP "" 2 + --new-line-format=\(aq> %l +.IP "" 2 +\(aq \\ +.IP "" 2 + --old-group-format=\(aq%df%(f=l?:,%dl)d%dE +.IP "" 2 +%<\(aq \\ +.IP "" 2 + --new-group-format=\(aq%dea%dF%(F=L?:,%dL) +.IP "" 2 +%>\(aq \\ +.IP "" 2 + --changed-group-format=\(aq%df%(f=l?:,%dl)c%dF%(F=L?:,%dL) +.IP "" 2 +%<\(em +.IP "" 2 +%>\(aq \\ +.IP "" 2 + --unchanged-group-format=\(aq\(aq \\ +.IP "" 2 + myfile + +.PD +.IP "" 0 +.SP +.SH "diff examples" +.SP +The following line produces a Unidiff (\fB-u\fR flag) +between revision 1.14 and 1.19 of +\fBbackend.c\fR. Due to the \fB-kk\fR flag no +keywords are substituted, so differences that only depend +on keyword substitution are ignored. +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs diff -kk -u -r 1.14 -r 1.19 backend.c + +.PD +.IP "" 0 +.SP +Suppose the experimental branch EXPR1 was based on a +set of files tagged RELEASE_1_0. To see what has +happened on that branch, the following can be used: +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs diff -r RELEASE_1_0 -r EXPR1 + +.PD +.IP "" 0 +.SP +A command like this can be used to produce a context +diff between two releases: +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs diff -c -r RELEASE_1_0 -r RELEASE_1_1 > diffs + +.PD +.IP "" 0 +.SP +If you are maintaining ChangeLogs, a command like the following +just before you commit your changes may help you write +the ChangeLog entry. All local modifications that have +not yet been committed will be printed. +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs diff -u | less + +.PD +.IP "" 0 +.SP +.SH "export" +.SS "Export sources from CVS, similar to checkout" +.IX "export (subcommand)" +.SP +.IP "\(bu" 2 +Synopsis: export [-flNnR] [-r rev|-D date] [-k subst] [-d dir] module\&... +.IP "\(bu" 2 +Requires: repository. +.IP "\(bu" 2 +Changes: current directory. +.SP +This command is a variant of \fBcheckout\fR; use it +when you want a copy of the source for module without +the \fBcvs\fR administrative directories. For example, you +might use \fBexport\fR to prepare source for shipment +off-site. This command requires that you specify a +date or tag (with \fB-D\fR or \fB-r\fR), so that you +can count on reproducing the source you ship to others +(and thus it always prunes empty directories). +.SP +One often would like to use \fB-kv\fR with \fBcvs +export\fR. This causes any keywords to be +expanded such that an import done at some other site +will not lose the keyword revision information. But be +aware that doesn\(aqt handle an export containing binary +files correctly. Also be aware that after having used +\fB-kv\fR, one can no longer use the \fBident\fR +command (which is part of the \fBrcs\fR suite\(emsee +ident(1)) which looks for keyword strings. If +you want to be able to use \fBident\fR you must not +use \fB-kv\fR. +.SP +.SH "export options" +.SP +These standard options are supported by \fBexport\fR +(see node `Common options\(aq in the CVS manual, for a complete description of +them): +.SP +.IP "" 0 +\fB-D \fIdate\fB\fR +.IP "" 2 +Use the most recent revision no later than \fIdate\fR. +.SP +.IP "" 0 +\fB-f\fR +.IP "" 2 +If no matching revision is found, retrieve the most +recent revision (instead of ignoring the file). +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local; run only in current working directory. +.SP +.IP "" 0 +\fB-n\fR +.IP "" 2 +Do not run any checkout program. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Export directories recursively. This is on by default. +.SP +.IP "" 0 +\fB-r \fItag\fB\fR +.IP "" 2 +Use revision \fItag\fR. +.SP +In addition, these options (that are common to +\fBcheckout\fR and \fBexport\fR) are also supported: +.SP +.IP "" 0 +\fB-d \fIdir\fB\fR +.IP "" 2 +Create a directory called \fIdir\fR for the working +files, instead of using the module name. +see node `checkout options\(aq in the CVS manual, for complete details on how +\fBcvs\fR handles this flag. +.SP +.IP "" 0 +\fB-k \fIsubst\fB\fR +.IP "" 2 +Set keyword expansion mode (see node `Substitution modes\(aq in the CVS manual). +.SP +.IP "" 0 +\fB-N\fR +.IP "" 2 +Only useful together with \fB-d \fIdir\fB\fR. +see node `checkout options\(aq in the CVS manual, for complete details on how +\fBcvs\fR handles this flag. +.SP +.SH "history" +.SS "Show status of files and users" +.IX "history (subcommand)" +.SP +.IP "\(bu" 2 +Synopsis: history [-report] [-flags] [-options args] [files\&...] +.IP "\(bu" 2 +Requires: the file \fB$CVSROOT/CVSROOT/history\fR +.IP "\(bu" 2 +Changes: nothing. +.SP +\fBcvs\fR can keep a history file that tracks each use of the +\fBcheckout\fR, \fBcommit\fR, \fBrtag\fR, +\fBupdate\fR, and \fBrelease\fR commands. You can +use \fBhistory\fR to display this information in +various formats. +.SP +Logging must be enabled by creating the file +\fB$CVSROOT/CVSROOT/history\fR. +.SP +\fBNote: \fBhistory\fB uses \fB-f\fB, \fB-l\fB, +\fB-n\fB, and \fB-p\fB in ways that conflict with the +normal use inside \fBcvs\fB (see node `Common options\(aq in the CVS manual).\fR +.SP +.SH "history options" +.SP +Several options (shown above as \fB-report\fR) control what +kind of report is generated: +.SP +.IP "" 0 +\fB-c\fR +.IP "" 2 +Report on each time commit was used (i.e., each time +the repository was modified). +.SP +.IP "" 0 +\fB-e\fR +.IP "" 2 +Everything (all record types). Equivalent to +specifying \fB-x\fR with all record types. Of course, +\fB-e\fR will also include record types which are +added in a future version of \fBcvs\fR; if you are +writing a script which can only handle certain record +types, you\(aqll want to specify \fB-x\fR. +.SP +.IP "" 0 +\fB-m \fImodule\fB\fR +.IP "" 2 +Report on a particular module. (You can meaningfully +use \fB-m\fR more than once on the command line.) +.SP +.IP "" 0 +\fB-o\fR +.IP "" 2 +Report on checked-out modules. This is the default report type. +.SP +.IP "" 0 +\fB-T\fR +.IP "" 2 +Report on all tags. +.SP +.IP "" 0 +\fB-x \fItype\fB\fR +.IP "" 2 +Extract a particular set of record types \fItype\fR from the \fBcvs\fR +history. The types are indicated by single letters, +which you may specify in combination. +.SP +Certain commands have a single record type: +.SP +.IP "" 2 +\fBF\fR +.IP "" 4 +release +.IP "" 2 +\fBO\fR +.IP "" 4 +checkout +.IP "" 2 +\fBE\fR +.IP "" 4 +export +.IP "" 2 +\fBT\fR +.IP "" 4 +rtag +.SP +One of five record types may result from an update: +.SP +.IP "" 2 +\fBC\fR +.IP "" 4 +A merge was necessary but collisions were +detected (requiring manual merging). +.IP "" 2 +\fBG\fR +.IP "" 4 +A merge was necessary and it succeeded. +.IP "" 2 +\fBU\fR +.IP "" 4 +A working file was copied from the repository. +.IP "" 2 +\fBP\fR +.IP "" 4 +A working file was patched to match the repository. +.IP "" 2 +\fBW\fR +.IP "" 4 +The working copy of a file was deleted during +update (because it was gone from the repository). +.SP +One of three record types results from commit: +.SP +.IP "" 2 +\fBA\fR +.IP "" 4 +A file was added for the first time. +.IP "" 2 +\fBM\fR +.IP "" 4 +A file was modified. +.IP "" 2 +\fBR\fR +.IP "" 4 +A file was removed. +.SP +The options shown as \fB-flags\fR constrain or expand +the report without requiring option arguments: +.SP +.IP "" 0 +\fB-a\fR +.IP "" 2 +Show data for all users (the default is to show data +only for the user executing \fBhistory\fR). +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Show last modification only. +.SP +.IP "" 0 +\fB-w\fR +.IP "" 2 +Show only the records for modifications done from the +same working directory where \fBhistory\fR is +executing. +.SP +The options shown as \fB-options \fIargs\fB\fR constrain the report +based on an argument: +.SP +.IP "" 0 +\fB-b \fIstr\fB\fR +.IP "" 2 +Show data back to a record containing the string +\fIstr\fR in either the module name, the file name, or +the repository path. +.SP +.IP "" 0 +\fB-D \fIdate\fB\fR +.IP "" 2 +Show data since \fIdate\fR. This is slightly different +from the normal use of \fB-D \fIdate\fB\fR, which +selects the newest revision older than \fIdate\fR. +.SP +.IP "" 0 +\fB-f \fIfile\fB\fR +.IP "" 2 +Show data for a particular file +(you can specify several \fB-f\fR options on the same command line). +This is equivalent to specifying the file on the command line. +.SP +.IP "" 0 +\fB-n \fImodule\fB\fR +.IP "" 2 +Show data for a particular module +(you can specify several \fB-n\fR options on the same command line). +.SP +.IP "" 0 +\fB-p \fIrepository\fB\fR +.IP "" 2 +Show data for a particular source repository (you +can specify several \fB-p\fR options on the same command +line). +.SP +.IP "" 0 +\fB-r \fIrev\fB\fR +.IP "" 2 +Show records referring to revisions since the revision +or tag named \fIrev\fR appears in individual \fBrcs\fR +files. Each \fBrcs\fR file is searched for the revision or +tag. +.SP +.IP "" 0 +\fB-t \fItag\fB\fR +.IP "" 2 +Show records since tag \fItag\fR was last added to the +history file. This differs from the \fB-r\fR flag +above in that it reads only the history file, not the +\fBrcs\fR files, and is much faster. +.SP +.IP "" 0 +\fB-u \fIname\fB\fR +.IP "" 2 +Show records for user \fIname\fR. +.SP +.IP "" 0 +\fB-z \fItimezone\fB\fR +.IP "" 2 +Show times in the selected records using the specified +time zone instead of UTC. +.SP +.SH "import" +.SS "Import sources into CVS, using vendor branches" +.IX "import (subcommand)" +.SP +.IP "\(bu" 2 +Synopsis: import [-options] repository vendortag releasetag\&... +.IP "\(bu" 2 +Requires: Repository, source distribution directory. +.IP "\(bu" 2 +Changes: repository. +.SP +Use \fBimport\fR to incorporate an entire source +distribution from an outside source (e.g., a source +vendor) into your source repository directory. You can +use this command both for initial creation of a +repository, and for wholesale updates to the module +from the outside source. see node `Tracking sources\(aq in the CVS manual, for +a discussion on this subject. +.SP +The \fIrepository\fR argument gives a directory name +(or a path to a directory) under the \fBcvs\fR root directory +for repositories; if the directory did not exist, +import creates it. +.SP +When you use import for updates to source that has been +modified in your source repository (since a prior +import), it will notify you of any files that conflict +in the two branches of development; use \fBcheckout +-j\fR to reconcile the differences, as import instructs +you to do. +.SP +If \fBcvs\fR decides a file should be ignored +(see node `cvsignore\(aq in the CVS manual), it does not import it and prints +\fBI \fR followed by the filename (see node `import output\(aq in the CVS manual, for a +complete description of the output). +.SP +If the file \fB$CVSROOT/CVSROOT/cvswrappers\fR exists, +any file whose names match the specifications in that +file will be treated as packages and the appropriate +filtering will be performed on the file/directory +before being imported. see node `Wrappers\(aq in the CVS manual. +.SP +The outside source is saved in a first-level +branch, by default 1.1.1. Updates are leaves of this +branch; for example, files from the first imported +collection of source will be revision 1.1.1.1, then +files from the first imported update will be revision +1.1.1.2, and so on. +.SP +At least three arguments are required. +\fIrepository\fR is needed to identify the collection +of source. \fIvendortag\fR is a tag for the entire +branch (e.g., for 1.1.1). You must also specify at +least one \fIreleasetag\fR to identify the files at +the leaves created each time you execute \fBimport\fR. +.SP +Note that \fBimport\fR does \fInot\fR change the +directory in which you invoke it. In particular, it +does not set up that directory as a \fBcvs\fR working +directory; if you want to work with the sources import +them first and then check them out into a different +directory (see node `Getting the source\(aq in the CVS manual). +.SP +.SH "import options" +.SP +This standard option is supported by \fBimport\fR +(see node `Common options\(aq in the CVS manual, for a complete description): +.SP +.IP "" 0 +\fB-m \fImessage\fB\fR +.IP "" 2 +Use \fImessage\fR as log information, instead of +invoking an editor. +.SP +There are the following additional special options. +.SP +.IP "" 0 +\fB-b \fIbranch\fB\fR +.IP "" 2 +See see node `Multiple vendor branches\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-k \fIsubst\fB\fR +.IP "" 2 +Indicate the 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 see node `Substitution modes\(aq in the CVS manual, for a +list of valid \fB-k\fR settings. +.SP +.IP "" 0 +\fB-I \fIname\fB\fR +.IP "" 2 +Specify file names that should be ignored during +import. You can use this option repeatedly. To avoid +ignoring any files at all (even those ignored by +default), specify \`-I !\(aq. +.SP +\fIname\fR can be a file name pattern of the same type +that you can specify in the \fB.cvsignore\fR file. +see node `cvsignore\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-W \fIspec\fB\fR +.IP "" 2 +Specify file names that should be filtered during +import. You can use this option repeatedly. +.SP +\fIspec\fR can be a file name pattern of the same type +that you can specify in the \fB.cvswrappers\fR +file. see node `Wrappers\(aq in the CVS manual. +.SP +.SH "import output" +.SP +\fBimport\fR keeps you informed of its progress by printing a line +for each file, preceded by one character indicating the status of the file: +.SP +.IP "" 0 +\fBU \fIfile\fB\fR +.IP "" 2 +The file already exists in the repository and has not been locally +modified; a new revision has been created (if necessary). +.SP +.IP "" 0 +\fBN \fIfile\fB\fR +.IP "" 2 +The file is a new file which has been added to the repository. +.SP +.IP "" 0 +\fBC \fIfile\fB\fR +.IP "" 2 +The file already exists in the repository but has been locally modified; +you will have to merge the changes. +.SP +.IP "" 0 +\fBI \fIfile\fB\fR +.IP "" 2 +The file is being ignored (see node `cvsignore\(aq in the CVS manual). +.SP +.IX "Symbolic link, importing" +.IX "Link, symbolic, importing" +.IP "" 0 +\fBL \fIfile\fB\fR +.IP "" 2 +The file is a symbolic link; \fBcvs import\fR 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 is not apparent. +(Various options in the \fBmodules\fR file can be used +to recreate symbolic links on checkout, update, etc.; +see node `modules\(aq in the CVS manual.) +.SP +.SH "import examples" +.SP +See see node `Tracking sources\(aq in the CVS manual, and see node `From files\(aq in the CVS manual. +.SP +.SH "log" +.SS "Print out log information for files" +.IX "log (subcommand)" +.SP +.IP "\(bu" 2 +Synopsis: log [options] [files\&...] +.IP "\(bu" 2 +Requires: repository, working directory. +.IP "\(bu" 2 +Changes: nothing. +.SP +Display log information for files. \fBlog\fR used to +call the \fBrcs\fR utility \fBrlog\fR. 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 \fBcvs\fR +commands. +.SP +.IX "Timezone, in output" +.IX "Zone, time, in output" +The output includes the location of the \fBrcs\fR file, +the \fIhead\fR revision (the latest revision on the +trunk), all symbolic names (tags) and some other +things. For each revision, the revision number, the +date, the +author, the number of lines added/deleted and the log +message are printed. All dates are displayed in local +time at the client. This is typically specified in the +\fB$TZ\fR environment variable, which can be set to +govern how \fBlog\fR displays dates. +.SP +\fBNote: \fBlog\fB uses \fB-R\fB in a way that conflicts +with the normal use inside \fBcvs\fB (see node `Common options\(aq in the CVS manual).\fR +.SP +.SH "log options" +.SP +By default, \fBlog\fR prints all information that is +available. All other options restrict the output. +.SP +.IP "" 0 +\fB-b\fR +.IP "" 2 +Print information about the revisions on the default +branch, normally the highest branch on the trunk. +.SP +.IP "" 0 +\fB-d \fIdates\fB\fR +.IP "" 2 +Print information about revisions with a checkin +date/time in the range given by the +semicolon-separated list of dates. The date formats +accepted are those accepted by the \fB-D\fR option to +many other \fBcvs\fR commands (see node `Common options\(aq in the CVS manual). +Dates can be combined into ranges as follows: +.SP +.IP "" 2 +\fB\fId1\fB<\fId2\fB\fR +.IP "" 4 +.IP "" 2 +\fB\fId2\fB>\fId1\fB\fR +.IP "" 4 +Select the revisions that were deposited between +\fId1\fR and \fId2\fR. +.SP +.IP "" 2 +\fB<\fId\fB\fR +.IP "" 4 +.IP "" 2 +\fB\fId\fB>\fR +.IP "" 4 +Select all revisions dated \fId\fR or earlier. +.SP +.IP "" 2 +\fB\fId\fB<\fR +.IP "" 4 +.IP "" 2 +\fB>\fId\fB\fR +.IP "" 4 +Select all revisions dated \fId\fR or later. +.SP +.IP "" 2 +\fB\fId\fB\fR +.IP "" 4 +Select the single, latest revision dated \fId\fR or +earlier. +.SP +The \fB>\fR or \fB<\fR characters may be followed by +\fB=\fR to indicate an inclusive range rather than an +exclusive one. +.SP +Note that the separator is a semicolon (;). +.SP +.IP "" 0 +\fB-h\fR +.IP "" 2 +Print only the name of the \fBrcs\fR file, name +of the file in the working directory, head, +default branch, access list, locks, symbolic names, and +suffix. +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local; run only in current working directory. (Default +is to run recursively). +.SP +.IP "" 0 +\fB-N\fR +.IP "" 2 +Do not print the list of tags for this file. This +option can be very useful when your site uses a lot of +tags, so rather than "more"\(aqing over 3 pages of tag +information, the log information is presented without +tags at all. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Print only the name of the \fBrcs\fR file. +.SP +.IP "" 0 +\fB-r\fIrevisions\fB\fR +.IP "" 2 +Print information about revisions given in the +comma-separated list \fIrevisions\fR of revisions and +ranges. The following table explains the available +range formats: +.SP +.IP "" 2 +\fB\fIrev1\fB:\fIrev2\fB\fR +.IP "" 4 +Revisions \fIrev1\fR to \fIrev2\fR (which must be on +the same branch). +.SP +.IP "" 2 +\fB\fIrev1\fB::\fIrev2\fB\fR +.IP "" 4 +The same, but excluding \fIrev1\fR. +.SP +.IP "" 2 +\fB:\fIrev\fB\fR +.IP "" 4 +.IP "" 2 +\fB::\fIrev\fB\fR +.IP "" 4 +Revisions from the beginning of the branch up to +and including \fIrev\fR. +.SP +.IP "" 2 +\fB\fIrev\fB:\fR +.IP "" 4 +Revisions starting with \fIrev\fR to the end of the +branch containing \fIrev\fR. +.SP +.IP "" 2 +\fB\fIrev\fB::\fR +.IP "" 4 +Revisions starting just after \fIrev\fR to the end of the +branch containing \fIrev\fR. +.SP +.IP "" 2 +\fB\fIbranch\fB\fR +.IP "" 4 +An argument that is a branch means all revisions on +that branch. +.SP +.IP "" 2 +\fB\fIbranch1\fB:\fIbranch2\fB\fR +.IP "" 4 +.IP "" 2 +\fB\fIbranch1\fB::\fIbranch2\fB\fR +.IP "" 4 +A range of branches means all revisions +on the branches in that range. +.SP +.IP "" 2 +\fB\fIbranch\fB.\fR +.IP "" 4 +The latest revision in \fIbranch\fR. +.SP +A bare \fB-r\fR with no revisions means the latest +revision on the default branch, normally the trunk. +There can be no space between the \fB-r\fR option and +its argument. +.SP +.IP "" 0 +\fB-S\fR +.IP "" 2 +Suppress the header if no revisions are selected. +.SP +.IP "" 0 +\fB-s \fIstates\fB\fR +.IP "" 2 +Print information about revisions whose state +attributes match one of the states given in the +comma-separated list \fIstates\fR. +.SP +.IP "" 0 +\fB-t\fR +.IP "" 2 +Print the same as \fB-h\fR, plus the descriptive text. +.SP +.IP "" 0 +\fB-w\fIlogins\fB\fR +.IP "" 2 +Print information about revisions checked in by users +with login names appearing in the comma-separated list +\fIlogins\fR. If \fIlogins\fR is omitted, the user\(aqs +login is assumed. There can be no space between the +\fB-w\fR option and its argument. +.SP +\fBlog\fR prints the intersection of the revisions +selected with the options \fB-d\fR, \fB-s\fR, and +\fB-w\fR, intersected with the union of the revisions +selected by \fB-b\fR and \fB-r\fR. +.SP +.SH "log examples" +.SP +.IX "Timezone, in output" +.IX "Zone, time, in output" +Since \fBlog\fR shows dates in local time, +you might want to see them in Coordinated Universal Time (UTC) or +some other timezone. +To do this you can set your \fB$TZ\fR environment +variable before invoking \fBcvs\fR: +.SP +.PD 0 +.SP +.IP "" 2 +$ TZ=UTC cvs log foo.c +.IP "" 2 +$ TZ=EST cvs log bar.c + +.PD +.IP "" 0 +.SP +(If you are using a \fBcsh\fR-style shell, like \fBtcsh\fR, +you would need to prefix the examples above with \fBenv\fR.) +.SP +.SH "ls & rls" +.IX "ls (subcommand)" +.IX "rls (subcommand)" +.SP +.IP "\(bu" 2 +ls [-e | -l] [-RP] [-r revision] [-D date] [path\&...] +.IP "\(bu" 2 +Requires: repository for \fBrls\fR, repository & working directory for +\fBls\fR. +.IP "\(bu" 2 +Changes: nothing. +.IP "\(bu" 2 +Synonym: \fBdir\fR & \fBlist\fR are synonyms for \fBls\fR and \fBrdir\fR +& \fBrlist\fR are synonyms for \fBrls\fR. +.SP +The \fBls\fR and \fBrls\fR commands are used to list +files and directories in the repository. +.SP +By default \fBls\fR lists the files and directories +that belong in your working directory, what would be +there after an \fBupdate\fR. +.SP +By default \fBrls\fR lists the files and directories +on the tip of the trunk in the topmost directory of the +repository. +.SP +Both commands accept an optional list of file and +directory names, relative to the working directory for +\fBls\fR and the topmost directory of the repository +for \fBrls\fR. Neither is recursive by default. +.SP +.SH "ls & rls options" +.SP +These standard options are supported by \fBls\fR & \fBrls\fR: +.SP +.IP "" 0 +\fB-d\fR +.IP "" 2 +Show dead revisions (with tag when specified). +.SP +.IP "" 0 +\fB-e\fR +.IP "" 2 +Display in CVS/Entries format. This format is meant to remain easily parsable +by automation. +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Display all details. +.SP +.IP "" 0 +\fB-P\fR +.IP "" 2 +Don\(aqt list contents of empty directories when recursing. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +List recursively. +.SP +.IP "" 0 +\fB-r \fIrevision\fB\fR +.IP "" 2 +Show files with revision or tag. +.SP +.IP "" 0 +\fB-D \fIdate\fB\fR +.IP "" 2 +Show files from date. +.SP +.SH "rls examples" +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs rls +.IP "" 2 +cvs rls: Listing module: \`.\(aq +.IP "" 2 +CVSROOT +.IP "" 2 +first-dir + +.PD +.IP "" 0 +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs rls CVSROOT +.IP "" 2 +cvs rls: Listing module: \`CVSROOT\(aq +.IP "" 2 +checkoutlist +.IP "" 2 +commitinfo +.IP "" 2 +config +.IP "" 2 +cvswrappers +.IP "" 2 +loginfo +.IP "" 2 +modules +.IP "" 2 +notify +.IP "" 2 +rcsinfo +.IP "" 2 +taginfo +.IP "" 2 +verifymsg +.SP + +.PD +.IP "" 0 +.SP +.SH "rdiff" +.SS "\(aqpatch\(aq format diffs between releases" +.IX "rdiff (subcommand)" +.SP +.IP "\(bu" 2 +rdiff [-flags] [-V vn] [-r t|-D d [-r t2|-D d2]] modules\&... +.IP "\(bu" 2 +Requires: repository. +.IP "\(bu" 2 +Changes: nothing. +.IP "\(bu" 2 +Synonym: patch +.SP +Builds a Larry Wall format patch(1) file between two +releases, that can be fed directly into the \fBpatch\fR +program to bring an old release up-to-date with the new +release. (This is one of the few \fBcvs\fR commands that +operates directly from the repository, and doesn\(aqt +require a prior checkout.) The diff output is sent to +the standard output device. +.SP +You can specify (using the standard \fB-r\fR and +\fB-D\fR options) any combination of one or two +revisions or dates. If only one revision or date is +specified, the patch file reflects differences between +that revision or date and the current head revisions in +the \fBrcs\fR file. +.SP +Note that if the software release affected is contained +in more than one directory, then it may be necessary to +specify the \fB-p\fR option to the \fBpatch\fR command when +patching the old sources, so that \fBpatch\fR is able to find +the files that are located in other directories. +.SP +.SH "rdiff options" +.SP +These standard options are supported by \fBrdiff\fR +(see node `Common options\(aq in the CVS manual, for a complete description of +them): +.SP +.IP "" 0 +\fB-D \fIdate\fB\fR +.IP "" 2 +Use the most recent revision no later than \fIdate\fR. +.SP +.IP "" 0 +\fB-f\fR +.IP "" 2 +If no matching revision is found, retrieve the most +recent revision (instead of ignoring the file). +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local; don\(aqt descend subdirectories. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Examine directories recursively. This option is on by default. +.SP +.IP "" 0 +\fB-r \fItag\fB\fR +.IP "" 2 +Use revision \fItag\fR. +.SP +In addition to the above, these options are available: +.SP +.IP "" 0 +\fB-c\fR +.IP "" 2 +Use the context diff format. This is the default format. +.SP +.IP "" 0 +\fB-s\fR +.IP "" 2 +Create a summary change report instead of a patch. The +summary includes information about files that were +changed or added between the releases. It is sent to +the standard output device. This is useful for finding +out, for example, which files have changed between two +dates or revisions. +.SP +.IP "" 0 +\fB-t\fR +.IP "" 2 +A diff of the top two revisions is sent to the standard +output device. This is most useful for seeing what the +last change to a file was. +.SP +.IP "" 0 +\fB-u\fR +.IP "" 2 +Use the unidiff format for the context diffs. +Remember that old versions +of the \fBpatch\fR program can\(aqt handle the unidiff +format, so if you plan to post this patch to the net +you should probably not use \fB-u\fR. +.SP +.IP "" 0 +\fB-V \fIvn\fB\fR +.IP "" 2 +Expand keywords according to the rules current in +\fBrcs\fR version \fIvn\fR (the expansion format changed with +\fBrcs\fR version 5). Note that this option is no +longer accepted. \fBcvs\fR will always expand keywords the +way that \fBrcs\fR version 5 does. +.SP +.SH "rdiff examples" +.SP +Suppose you receive mail from \fRfoo@example.net\fR asking for an +update from release 1.2 to 1.4 of the tc compiler. You +have no such patches on hand, but with \fBcvs\fR that can +easily be fixed with a command such as this: +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs rdiff -c -r FOO1_2 -r FOO1_4 tc | \\ +.IP "" 2 +$$ Mail -s \(aqThe patches you asked for\(aq foo@example.net + +.PD +.IP "" 0 +.SP +Suppose you have made release 1.3, and forked a branch +called \fBR_1_3fix\fR for bug fixes. \fBR_1_3_1\fR +corresponds to release 1.3.1, which was made some time +ago. Now, you want to see how much development has been +done on the branch. This command can be used: +.SP +.PD 0 +.SP +.IP "" 2 +$ cvs patch -s -r R_1_3_1 -r R_1_3fix module-name +.IP "" 2 +cvs rdiff: Diffing module-name +.IP "" 2 +File ChangeLog,v changed from revision 1.52.2.5 to 1.52.2.6 +.IP "" 2 +File foo.c,v changed from revision 1.52.2.3 to 1.52.2.4 +.IP "" 2 +File bar.h,v changed from revision 1.29.2.1 to 1.2 + +.PD +.IP "" 0 +.SP +.SH "release" +.SS "Indicate that a Module is no longer in use" +.IX "release (subcommand)" +.SP +.IP "\(bu" 2 +release [-d] directories\&... +.IP "\(bu" 2 +Requires: Working directory. +.IP "\(bu" 2 +Changes: Working directory, history log. +.SP +This command is meant to safely cancel the effect of +\fBcvs checkout\fR. Since \fBcvs\fR doesn\(aqt lock files, it +isn\(aqt strictly necessary to use this command. You can +always simply delete your working directory, if you +like; but you risk losing changes you may have +forgotten, and you leave no trace in the \fBcvs\fR history +file (see node `history file\(aq in the CVS manual) that you\(aqve abandoned your +checkout. +.SP +Use \fBcvs release\fR to avoid these problems. This +command checks that no uncommitted changes are +present; that you are executing it from immediately +above a \fBcvs\fR working directory; and that the repository +recorded for your files is the same as the repository +defined in the module database. +.SP +If all these conditions are true, \fBcvs release\fR +leaves a record of its execution (attesting to your +intentionally abandoning your checkout) in the \fBcvs\fR +history log. +.SP +.SH "release options" +.SP +The \fBrelease\fR command supports one command option: +.SP +.IP "" 0 +\fB-d\fR +.IP "" 2 +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. +.SP +\fBWARNING: The \fBrelease\fB 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 \fBadd\fB +command; see node `Adding files\(aq in the CVS manual) will be silently deleted\(emeven +if it is non-empty!\fR +.SP +.SH "release output" +.SP +Before \fBrelease\fR releases your sources it will +print a one-line message for any file that is not +up-to-date. +.SP +.IP "" 0 +\fBU \fIfile\fB\fR +.IP "" 2 +.IP "" 0 +\fBP \fIfile\fB\fR +.IP "" 2 +There exists a newer revision of this file in the +repository, and you have not modified your local copy +of the file (\fBU\fR and \fBP\fR mean the same thing). +.SP +.IP "" 0 +\fBA \fIfile\fB\fR +.IP "" 2 +The file has been added to your private copy of the +sources, but has not yet been committed to the +repository. If you delete your copy of the sources +this file will be lost. +.SP +.IP "" 0 +\fBR \fIfile\fB\fR +.IP "" 2 +The file has been removed from your private copy of the +sources, but has not yet been removed from the +repository, since you have not yet committed the +removal. see node `commit\(aq in the CVS manual. +.SP +.IP "" 0 +\fBM \fIfile\fB\fR +.IP "" 2 +The file is modified in your working directory. There +might also be a newer revision inside the repository. +.SP +.IP "" 0 +\fB? \fIfile\fB\fR +.IP "" 2 +\fIfile\fR is in your working directory, but does not +correspond to anything in the source repository, and is +not in the list of files for \fBcvs\fR to ignore (see the +description of the \fB-I\fR option, and +see node `cvsignore\(aq in the CVS manual). If you remove your working +sources, this file will be lost. +.SP +.SH "release examples" +.SP +Release the \fBtc\fR directory, and delete your local working copy +of the files. +.SP +.PD 0 +.SP +.IP "" 2 +$ cd \&.. # \fRYou must stand immediately above the\fR +.IP "" 2 + # \fRsources when you issue \fBcvs release\fR.\fR +.IP "" 2 +$ cvs release -d tc +.IP "" 2 +You have [0] altered files in this repository. +.IP "" 2 +Are you sure you want to release (and delete) directory \`tc\(aq: y +.IP "" 2 +$ + +.PD +.IP "" 0 +.SP +.SH "update" +.SS "Bring work tree in sync with repository" +.IX "update (subcommand)" +.SP +.IP "\(bu" 2 +update [-ACdflPpR] [-I name] [-j rev [-j rev]] [-k kflag] [-r tag|-D date] [-W spec] files\&... +.IP "\(bu" 2 +Requires: repository, working directory. +.IP "\(bu" 2 +Changes: working directory. +.SP +After you\(aqve run checkout to create your private copy +of source from the common repository, other developers +will continue changing the central source. From time +to time, when it is convenient in your development +process, you can use the \fBupdate\fR command from +within your working directory to reconcile your work +with any revisions applied to the source repository +since your last checkout or update. Without the \fB-C\fR +option, \fBupdate\fR will also merge any differences +between the local copy of files and their base revisions +into any destination revisions specified with \fB-r\fR, +\fB-D\fR, or \fB-A\fR. +.SP +.SH "update options" +.SP +These standard options are available with \fBupdate\fR +(see node `Common options\(aq in the CVS manual, for a complete description of +them): +.SP +.IP "" 0 +\fB-D date\fR +.IP "" 2 +Use the most recent revision no later than \fIdate\fR. +This option is sticky, and implies \fB-P\fR. +See see node `Sticky tags\(aq in the CVS manual, for more information on sticky tags/dates. +.SP +.IP "" 0 +\fB-f\fR +.IP "" 2 +Only useful with the \fB-D \fIdate\fB\fR or \fB-r +\fItag\fB\fR flags. If no matching revision is found, +retrieve the most recent revision (instead of ignoring +the file). +.SP +.IP "" 0 +\fB-k \fIkflag\fB\fR +.IP "" 2 +Process keywords according to \fIkflag\fR. See +see node `Keyword substitution\(aq in the CVS manual. +This option is sticky; future updates of +this file in this working directory will use the same +\fIkflag\fR. The \fBstatus\fR command can be viewed +to see the sticky options. See see node `Invoking CVS\(aq in the CVS manual, for +more information on the \fBstatus\fR command. +.SP +.IP "" 0 +\fB-l\fR +.IP "" 2 +Local; run only in current working directory. see node `Recursive behavior\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-P\fR +.IP "" 2 +Prune empty directories. See see node `Moving directories\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-p\fR +.IP "" 2 +Pipe files to the standard output. +.SP +.IP "" 0 +\fB-R\fR +.IP "" 2 +Update directories recursively (default). see node `Recursive +behavior\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-r rev\fR +.IP "" 2 +Retrieve revision/tag \fIrev\fR. This option is sticky, +and implies \fB-P\fR. +See see node `Sticky tags\(aq in the CVS manual, for more information on sticky tags/dates. +.SP +These special options are also available with +\fBupdate\fR. +.SP +.IP "" 0 +\fB-A\fR +.IP "" 2 +Reset any sticky tags, dates, or \fB-k\fR options. +See see node `Sticky tags\(aq in the CVS manual, for more information on sticky tags/dates. +.SP +.IP "" 0 +\fB-C\fR +.IP "" 2 +Overwrite locally modified files with clean copies from +the repository (the modified file is saved in +\fB.#\fIfile\fB.\fIrevision\fB\fR, however). +.SP +.IP "" 0 +\fB-d\fR +.IP "" 2 +Create any directories that exist in the repository if +they\(aqre missing from the working directory. Normally, +\fBupdate\fR acts only on directories and files that +were already enrolled in your working directory. +.SP +This is useful for updating directories that were +created in the repository since the initial checkout; +but it has an unfortunate side effect. If you +deliberately avoided certain directories in the +repository when you created your working directory +(either through use of a module name or by listing +explicitly the files and directories you wanted on the +command line), then updating with \fB-d\fR will create +those directories, which may not be what you want. +.SP +.IP "" 0 +\fB-I \fIname\fB\fR +.IP "" 2 +Ignore files whose names match \fIname\fR (in your +working directory) during the update. You can specify +\fB-I\fR more than once on the command line to specify +several files to ignore. Use \fB-I !\fR to avoid +ignoring any files at all. see node `cvsignore\(aq in the CVS manual, for other +ways to make \fBcvs\fR ignore some files. +.SP +.IP "" 0 +\fB-W\fIspec\fB\fR +.IP "" 2 +Specify file names that should be filtered during +update. You can use this option repeatedly. +.SP +\fIspec\fR can be a file name pattern of the same type +that you can specify in the \fB.cvswrappers\fR +file. see node `Wrappers\(aq in the CVS manual. +.SP +.IP "" 0 +\fB-j\fIrevision\fB\fR +.IP "" 2 +With two \fB-j\fR options, merge changes from the +revision specified with the first \fB-j\fR option to +the revision specified with the second \fBj\fR option, +into the working directory. +.SP +With one \fB-j\fR option, merge changes from the +ancestor revision to the revision specified with the +\fB-j\fR option, into the working directory. The +ancestor revision is the common ancestor of the +revision which the working directory is based on, and +the revision specified in the \fB-j\fR option. +.SP +Note that using a single \fB-j \fItagname\fB\fR option rather than +\fB-j \fIbranchname\fB\fR to merge changes from a branch will +often not remove files which were removed on the branch. +see node `Merging adds and removals\(aq in the CVS manual, for more. +.SP +In addition, each \fB-j\fR option can contain an optional +date specification which, when used with branches, can +limit the chosen revision to one within a specific +date. An optional date is specified by adding a colon +(:) to the tag: +\fB-j\fISymbolic_Tag\fB:\fIDate_Specifier\fB\fR. +.SP +see node `Branching and merging\(aq in the CVS manual. +.SP +.SP +.SH "update output" +.SP +\fBupdate\fR and \fBcheckout\fR keep you informed of +their progress by printing a line for each file, preceded +by one character indicating the status of the file: +.SP +.IP "" 0 +\fBU \fIfile\fB\fR +.IP "" 2 +The file was brought up to date with respect to the +repository. This is done for any file that exists in +the repository but not in your source, and for files +that you haven\(aqt changed but are not the most recent +versions available in the repository. +.SP +.IP "" 0 +\fBP \fIfile\fB\fR +.IP "" 2 +Like \fBU\fR, but the \fBcvs\fR server sends a patch instead of an entire +file. This accomplishes the same thing as \fBU\fR using less bandwidth. +.SP +.IP "" 0 +\fBA \fIfile\fB\fR +.IP "" 2 +The file has been added to your private copy of the +sources, and will be added to the source repository +when you run \fBcommit\fR on the file. This is a +reminder to you that the file needs to be committed. +.SP +.IP "" 0 +\fBR \fIfile\fB\fR +.IP "" 2 +The file has been removed from your private copy of the +sources, and will be removed from the source repository +when you run \fBcommit\fR on the file. This is a +reminder to you that the file needs to be committed. +.SP +.IP "" 0 +\fBM \fIfile\fB\fR +.IP "" 2 +The file is modified in your working directory. +.SP +\fBM\fR can indicate one of two states for a file +you\(aqre working on: either there were no modifications +to the same file in the repository, so that your file +remains as you last saw it; or there were modifications +in the repository as well as in your copy, but they +were merged successfully, without conflict, in your +working directory. +.SP +\fBcvs\fR will print some messages if it merges your work, +and a backup copy of your working file (as it looked +before you ran \fBupdate\fR) will be made. The exact +name of that file is printed while \fBupdate\fR runs. +.SP +.IP "" 0 +\fBC \fIfile\fB\fR +.IP "" 2 +.IX "\&.# files" +.IX "__ files (VMS)" +A conflict was detected while trying to merge your +changes to \fIfile\fR with changes from the source +repository. \fIfile\fR (the copy in your working +directory) is now the result of attempting to merge +the two revisions; an unmodified copy of your file +is also in your working directory, with the name +\fB.#\fIfile\fB.\fIrevision\fB\fR where \fIrevision\fR +is the revision that your modified file started +from. Resolve the conflict as described in +see node `Conflicts example\(aq in the CVS manual. +(Note that some systems automatically purge +files that begin with \fB.#\fR 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.) Under \fBvms\fR, the file name starts with +\fB__\fR rather than \fB.#\fR. +.SP +.IP "" 0 +\fB? \fIfile\fB\fR +.IP "" 2 +\fIfile\fR is in your working directory, but does not +correspond to anything in the source repository, and is +not in the list of files for \fBcvs\fR to ignore (see the +description of the \fB-I\fR option, and +see node `cvsignore\(aq in the CVS manual). +.SH "AUTHORS" +.TP +Dick Grune +Original author of the +.B cvs +shell script version posted to +.B comp.sources.unix +in the volume6 release of December, 1986. +Credited with much of the +.B cvs +conflict resolution algorithms. +.TP +Brian Berliner +Coder and designer of the +.B cvs +program itself in April, 1989, based on the original work done by Dick. +.TP +Jeff Polk +Helped Brian with the design of the +.B cvs +module and vendor branch support and author of the +.BR checkin ( 1 ) +shell script (the ancestor of \fBcvs import\fP). +.TP +Larry Jones, Derek R. Price, and Mark D. Baushke +Have helped maintain +.B cvs +for many years. +.TP +And many others too numerous to mention here. +.SH "SEE ALSO" +The most comprehensive manual for CVS is +Version Management with CVS by Per Cederqvist et al. Depending on +your system, you may be able to get it with the +.B info CVS +command or it may be available as cvs.pdf (Portable Document Format), +cvs.ps (PostScript), cvs.texinfo (Texinfo source), or cvs.html. +.SP +For CVS updates, more information on documentation, software related +to CVS, development of CVS, and more, see: +.in +1i +.SP +.PD 0 +.IP "" 4 +.B http://cvshome.org +.IP "" 4 +.B http://www.loria.fr/~molli/cvs-index.html +.in -1i +.SP +.BR ci ( 1 ), +.BR co ( 1 ), +.BR cvs ( 5 ), +.BR cvsbug ( 8 ), +.BR diff ( 1 ), +.BR grep ( 1 ), +.BR patch ( 1 ), +.BR rcs ( 1 ), +.BR rcsdiff ( 1 ), +.BR rcsmerge ( 1 ), +.BR rlog ( 1 ). diff --git a/contrib/cvs-1.12.9/doc/cvs.texinfo b/contrib/cvs-1.12.9/doc/cvs.texinfo new file mode 100644 index 0000000000..2061bbb088 --- /dev/null +++ b/contrib/cvs-1.12.9/doc/cvs.texinfo @@ -0,0 +1,15145 @@ +\input texinfo @c -*-texinfo-*- +@comment Documentation for CVS. +@setfilename cvs.info +@macro copyleftnotice +@noindent +Copyright @copyright{} 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +@multitable @columnfractions .12 .88 +@item Portions +@item @tab Copyright @copyright{} 1999, 2000, 2001, 2002, 2003, 2004 + Derek R. Price, +@item @tab Copyright @copyright{} 2002, 2003, 2004 + Ximbiot @url{http://ximbiot.com}, +@item @tab Copyright @copyright{} 1992, 1993, 1999 Signum Support AB, +@item @tab and Copyright @copyright{} others. +@end multitable + +@ignore +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation +approved by the Free Software Foundation. +@end macro + +@comment This file is part of the CVS distribution. + +@comment CVS is free software; you can redistribute it and/or modify +@comment it under the terms of the GNU General Public License as published by +@comment the Free Software Foundation; either version 2, or (at your option) +@comment any later version. + +@comment CVS is distributed in the hope that it will be useful, +@comment but WITHOUT ANY WARRANTY; without even the implied warranty of +@comment MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +@comment GNU General Public License for more details. + +@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 (RFC2346). + +@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 + +@include version.texi +@settitle CVS---Concurrent Versions System v@value{VERSION} +@setchapternewpage odd + +@c -- TODO list: +@c -- Fix all lines that match "^@c -- " +@c -- Also places marked with FIXME should be manual +@c problems (as opposed to FIXCVS for CVS problems). + +@c @splitrcskeyword{} is used to avoid keyword expansion. It is replaced by +@c @asis when generating info and dvi, and by in the generated html, +@c such that keywords are not expanded in the generated html. +@ifnothtml +@macro splitrcskeyword {arg} +@asis{}\arg\ +@end macro +@end ifnothtml + +@ifhtml +@macro splitrcskeyword {arg} +@i{}\arg\ +@end macro +@end ifhtml + +@dircategory GNU Packages +@direntry +* CVS: (cvs). Concurrent Versions System +@end direntry +@dircategory Individual utilities +@direntry +* cvs: (cvs)CVS commands. Concurrent Versions System +@end direntry + +@comment The titlepage section does not appear in the Info file. +@titlepage +@sp 4 +@comment The title is printed in a large font. +@center @titlefont{Version Management} +@sp +@center @titlefont{with} +@sp +@center @titlefont{CVS} +@sp 2 +@center for @sc{cvs} @value{VERSION} +@comment -release- +@sp 3 +@center Per Cederqvist et al + +@comment The following two commands start the copyright page +@comment for the printed manual. This will not appear in the Info file. +@page +@vskip 0pt plus 1filll +@copyleftnotice +@end titlepage + +@comment ================================================================ +@comment The real text starts here +@comment ================================================================ + +@ifnottex +@c --------------------------------------------------------------------- +@node Top +@top + +This info manual describes how to use and administer +@sc{cvs} version @value{VERSION}. +@end ifnottex + +@ifinfo +@copyleftnotice +@end ifinfo + +@c This menu is pretty long. Not sure how easily that +@c can be fixed (no brilliant ideas right away)... +@menu +* Overview:: An introduction to CVS +* Repository:: Where all your sources are stored +* Starting a new project:: Starting a project with CVS +* Revisions:: Numeric and symbolic names for revisions +* Branching and merging:: Diverging/rejoining branches of development +* Recursive behavior:: CVS descends directories +* Adding and removing:: Adding/removing/renaming files/directories +* History browsing:: Viewing the history of files in various ways + +CVS and the Real World. +----------------------- +* Binary files:: CVS can handle binary files +* Multiple developers:: How CVS helps a group of developers +* Revision management:: Policy questions for revision management +* Keyword substitution:: CVS can include the revision inside the file +* Tracking sources:: Tracking third-party sources +* Builds:: Issues related to CVS and builds +* Special Files:: Devices, links and other non-regular files + +References. +----------- +* 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 +* Compatibility:: Upgrading CVS versions +* Troubleshooting:: Some tips when nothing works +* Credits:: Some of the contributors to this manual +* BUGS:: Dealing with bugs in CVS or this manual +* Index:: Index +@end menu + +@c --------------------------------------------------------------------- +@node Overview +@chapter Overview +@cindex Overview + +This chapter is for people who have never used +@sc{cvs}, and perhaps have never used version control +software before. + +If you are already familiar with @sc{cvs} and are just +trying to learn a particular feature or remember a +certain command, you can probably skip everything here. + +@menu +* What is CVS?:: What you can do with @sc{cvs} +* What is CVS not?:: Problems @sc{cvs} doesn't try to solve +* A sample session:: A tour of basic @sc{cvs} usage +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node What is CVS? +@section What is CVS? +@cindex What is CVS? +@cindex Introduction to CVS +@cindex CVS, introduction to + +@sc{cvs} is a version control system. Using it, you can +record the history of your source files. + +@c -- /// +@c -- ///Those who cannot remember the past are condemned to repeat it. +@c -- /// -- George Santayana +@c -- ////// + +@c -- Insert history quote here! +For example, bugs sometimes creep in when +software is modified, and you might not detect the bug +until a long time after you make the modification. +With @sc{cvs}, you can easily retrieve old versions to see +exactly which change caused the bug. This can +sometimes be a big help. + +You could of course save every version of every file +you have ever created. This would +however waste an enormous amount of disk space. @sc{cvs} +stores all the versions of a file in a single file in a +clever way that only stores the differences between +versions. + +@sc{cvs} also helps you if you are part of a group of people working +on the same project. It is all too easy to overwrite +each others' changes unless you are extremely careful. +Some editors, like @sc{gnu} Emacs, try to make sure that +the same file is never modified by two people at the +same time. Unfortunately, if someone is using another +editor, that safeguard will not work. @sc{cvs} solves this problem +by insulating the different developers from each other. Every +developer works in his own directory, and @sc{cvs} merges +the work when each developer is done. + +@cindex History of CVS +@cindex CVS, history of +@cindex Credits (CVS program) +@cindex Contributors (CVS program) +@sc{cvs} started out as a bunch of shell scripts written by +Dick Grune, posted to the newsgroup +@code{comp.sources.unix} in the volume 6 +release of July, 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 +come from them. + +In April, 1989, Brian Berliner designed and coded @sc{cvs}. +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} in a variety of ways, including +free download from the Internet. For more information +on downloading @sc{cvs} and other @sc{cvs} topics, see: + +@example +@url{http://www.cvshome.org/} +@url{http://www.loria.fr/~molli/cvs-index.html} +@end example + +@cindex Mailing list +@cindex List, mailing list +@cindex Newsgroups +There is a mailing list, known as @email{info-cvs@@gnu.org}, +devoted to @sc{cvs}. To subscribe or +unsubscribe +write to +@email{info-cvs-request@@gnu.org}. +If you prefer a Usenet group, there is a one-way mirror (posts to the email +list are usually sent to the news group, but not visa versa) of +@email{info-cvs@@gnu.org} at @url{news:gnu.cvs.help}. The right +Usenet group for posts is @url{news: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 +@url{news:comp.software.config-mgmt}. +@c Other random data is that the 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 @email{bug-cvs@@gnu.org} mailing list, +described in more detail in @ref{BUGS}. To subscribe +send mail to @email{bug-cvs-request@@gnu.org}. There is a two-way +Usenet mirror (posts to the Usenet group are usually sent to the email list and +visa versa) of @email{bug-cvs@@gnu.org} named @url{news:gnu.cvs.bug}. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node What is CVS not? +@section What is CVS not? +@cindex What is CVS not? + +@sc{cvs} can do a lot of things for you, but it does +not try to be everything for everyone. + +@table @asis +@item @sc{cvs} is not a build system. + +Though the structure of your repository and modules +file interact with your build system +(e.g. @file{Makefile}s), they are essentially +independent. + +@sc{cvs} does not dictate how you build anything. It +merely stores files for retrieval in a tree structure +you devise. + +@sc{cvs} does not dictate how to use disk space in the +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. + +If you modularize your work, and construct a build +system that will share files (via links, mounts, +@code{VPATH} in @file{Makefile}s, etc.), you can +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. + +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 +to you frequently enough to make certain you are aware +of schedules, merge points, branch names and release +dates. If they don't, @sc{cvs} can't help. + +@sc{cvs} is an instrument for making sources dance to +your tune. But you are the piper and the composer. No +instrument plays itself or writes its own music. + +@item @sc{cvs} is not a substitute for developer communication. + +When faced with conflicts within a single file, most +developers manage to resolve them without too much +effort. But a more general definition of ``conflict'' +includes problems too difficult to solve without +communication between developers. + +@sc{cvs} cannot determine when simultaneous changes +within a single file, or across a whole collection of +files, will logically conflict with one another. Its +concept of a @dfn{conflict} is purely textual, arising +when two changes to the same base file are near enough +to spook the merge (i.e. @code{diff3}) command. + +@sc{cvs} does not claim to help at all in figuring out +non-textual or distributed conflicts in program logic. + +For example: Say you change the arguments to function +@code{X} defined in file @file{A}. At the same time, +someone edits file @file{B}, adding new calls to +function @code{X} using the old arguments. You are +outside the realm of @sc{cvs}'s competence. + +Acquire the habit of reading specs and talking to your +peers. + + +@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 +test suite 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 built-in 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 + +@c --------------------------------------------------------------------- +@node A sample session +@section A sample session +@cindex Example of a work-session +@cindex Getting started +@cindex Work-session, example of +@cindex tc, Trivial Compiler (example) +@cindex Trivial Compiler (example) + +@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}. +The compiler is called @samp{tc} (Trivial Compiler), +and the repository is set up so that there is a module +called @samp{tc}. + +@menu +* Getting the source:: Creating a workspace +* Committing your changes:: Making your work available to others +* Cleaning up:: Cleaning up +* Viewing differences:: Viewing differences +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Getting the source +@subsection Getting the source +@cindex Getting the source +@cindex Checking out source +@cindex Fetching source +@cindex Source, getting from CVS +@cindex Checkout, example + +The first thing you must do is to get your own working copy of the +source for @samp{tc}. For this, you use the @code{checkout} command: + +@example +$ cvs checkout tc +@end example + +@noindent +This will create a new directory called @file{tc} and populate it with +the source files. + +@example +$ cd tc +$ ls +CVS Makefile backend.c driver.c frontend.c parser.c +@end example + +The @file{CVS} directory is used internally by +@sc{cvs}. Normally, you should not modify or remove +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. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Committing your changes +@subsection Committing your changes +@cindex Committing changes to files +@cindex Log message entry + +When you have checked that the compiler is still compilable you decide +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 +@end example + +@noindent +@sc{cvs} starts an editor, to allow you to enter a log +message. You type in ``Added an optimization pass.'', +save the temporary file, and exit the editor. + +@cindex CVSEDITOR, environment variable +@cindex EDITOR, environment variable +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 there is a default +which will vary with your operating system, for example +@code{vi} for unix or @code{notepad} for Windows +NT/95. + +@cindex VISUAL, environment variable +In addition, @sc{cvs} checks the @code{$VISUAL} environment +variable. Opinions vary on whether this behavior is desirable and +whether future releases of @sc{cvs} should check @code{$VISUAL} or +ignore it. You will be OK either way if you make sure that +@code{$VISUAL} is either unset or set to the same thing as +@code{$EDITOR}. + +@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. +@c FIXME: Need to talk more about the process of +@c prompting for the log message. Like show an example +@c of what it pops up in the editor, for example. Also +@c a discussion of how to get the "a)bort, c)ontinue, +@c e)dit" prompt and what to do with it. Might also +@c work in the suggestion that if you want a diff, you +@c should make it before running commit (someone +@c suggested that the diff pop up in the editor. I'm +@c not sure that is better than telling people to run +@c "cvs diff" first if that is what they want, but if +@c we want to tell people that, the manual possibly +@c should say it). + +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: + +@example +$ cvs commit -m "Added an optimization pass" backend.c +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Cleaning up +@subsection Cleaning up +@cindex Cleaning up +@cindex Working copy, removing +@cindex Removing your working copy +@cindex Releasing your working copy + +Before you turn to other tasks you decide to remove your working copy of +tc. One acceptable way to do that is of course + +@example +$ cd .. +$ rm -r tc +@end example + +@noindent +but a better way is to use the @code{release} command (@pxref{release}): + +@example +$ cd .. +$ cvs release -d tc +M driver.c +? tc +You have [1] altered files in this repository. +Are you sure you want to release (and delete) directory `tc': n +** `release' aborted by user choice. +@end example + +The @code{release} command checks that all your modifications have been +committed. If history logging is enabled it also makes a note in the +history file. @xref{history file}. + +When you use the @samp{-d} flag with @code{release}, it +also removes your working copy. + +In the example above, the @code{release} command wrote a couple of lines +of output. @samp{? tc} means that the file @file{tc} is unknown to @sc{cvs}. +That is nothing to worry about: @file{tc} is the executable compiler, +and it should not be stored in the repository. @xref{cvsignore}, +for information about how to make that warning go away. +@xref{release output}, for a complete explanation of +all possible output from @code{release}. + +@samp{M driver.c} is more serious. It means that the +file @file{driver.c} has been modified since it was +checked out. + +The @code{release} command always finishes by telling +you how many modified files you have in your working +copy of the sources, and then asks you for confirmation +before deleting any files or making any note in the +history file. + +You decide to play it safe and answer @kbd{n @key{RET}} +when @code{release} asks for confirmation. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Viewing differences +@subsection Viewing differences +@cindex Viewing differences +@cindex Diff + +You do not remember modifying @file{driver.c}, so you want to see what +has happened to that file. + +@example +$ cd tc +$ cvs diff driver.c +@end example + +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 +Checking in driver.c; +/usr/local/cvsroot/tc/driver.c,v <-- driver.c +new revision: 1.2; previous revision: 1.1 +done +$ cd .. +$ cvs release -d tc +? tc +You have [0] altered files in this repository. +Are you sure you want to release (and delete) directory `tc': y +@end example + +@c --------------------------------------------------------------------- +@node Repository +@chapter The Repository +@cindex Repository (intro) +@cindex Repository, example +@cindex Layout of repository +@cindex Typical repository +@cindex /usr/local/cvsroot, as example repository +@cindex cvsroot + +The @sc{cvs} @dfn{repository} stores a complete copy of +all the files and directories which are under version +control. + +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:, setting up +@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 +starts with @samp{/}, then @code{:local:} is +assumed. If it does not start with @samp{/} then 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 +* Moving a repository:: Moving 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 several ways to tell @sc{cvs} +where to find the repository. You can name the +repository on the command line explicitly, with the +@code{-d} (for "directory") option: + +@example +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. +To set @code{$CVSROOT}, @code{csh} and @code{tcsh} +users should have this line in their @file{.cshrc} or +@file{.tcshrc} files: + +@example +setenv CVSROOT /usr/local/cvsroot +@end example + +@noindent +@code{sh} and @code{bash} users should instead have these lines in their +@file{.profile} or @file{.bashrc}: + +@example +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 +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. If +@code{-d} option differs from @file{CVS/Root}, the +former is used. Of course, for proper operation they +should be two ways of referring to the same repository. + +@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 +* Repository files:: What files are stored in the repository +* File permissions:: File permissions +* Windows permissions:: Issues specific to Windows +* Attic:: Some files are stored in the Attic +* CVS in repository:: Additional information in CVS directory +* Locks:: CVS locks control concurrent accesses +* CVSROOT storage:: A few things about CVSROOT are different +@end menu + +@node Repository files +@subsection Where files are stored within the repository + +@c @cindex Filenames, legal +@c @cindex Legal filenames +@c Somewhere we need to say something about legitimate +@c characters in filenames in working directory and +@c repository. Not "/" (not even on non-unix). And +@c here is a specific set of issues: +@c Files starting with a - are handled inconsistently. They can not +@c be added to a repository with an add command, because it they are +@c interpreted as a switch. They can appear in a repository if they are +@c part of a tree that is imported. They can not be removed from the tree +@c once they are there. +@c Note that "--" *is* supported (as a +@c consequence of using GNU getopt). Should document +@c this somewhere ("Common options"?). The other usual technique, +@c "./-foo", isn't as effective, at least for "cvs add" +@c which doesn't support pathnames containing "/". + +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} + | + +--@t{yoyodyne} + | | + | +--@t{tc} + | | | + +--@t{Makefile,v} + +--@t{backend.c,v} + +--@t{driver.c,v} + +--@t{frontend.c,v} + +--@t{parser.c,v} + +--@t{man} + | | + | +--@t{tc.1,v} + | + +--@t{testing} + | + +--@t{testpgm.t,v} + +--@t{test2.t,v} +@end example + +@cindex History files +@cindex RCS history files +@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. 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)}, distributed with @sc{rcs}, or the +file @file{doc/RCSFILES} in the @sc{cvs} source +distribution. 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 Creating a repository or similar +@cindex Security, file permissions in repository +@cindex File permissions, general +@cindex Permissions, general +@c FIXME: we need to somehow reflect "permissions in +@c repository" versus "permissions in working +@c directory" in the index entries. +@cindex Group, UNIX file permissions, in repository +@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 +the persons that have permission to modify the files in +each directory. This normally means that you must +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. +(On some systems, you also need to set the set-group-ID-on-execution bit +on the repository directories (see chmod(1)) so that newly-created files +and directories get the group-ID of the parent directory rather than +that of the current process.) + +@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}). You can use LockDir in CVSROOT/config +to put the lock files somewhere other than in the repository +if you want to allow read-only access to some directories +(@pxref{config}). + +@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? +@cindex CVSROOT/val-tags file, and read-only access to projects +@cindex val-tags file, and read-only access to projects +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). + +Each @sc{rcs} file will be owned by the user who last +checked it in. This has little significance; what +really matters is who owns the directories. + +@cindex CVSUMASK, environment variable +@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. 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 @code{CVSREAD}, @ref{Environment variables}). +@c FIXME: Need more discussion of which +@c group should own the file in the repository. +@c Include a somewhat detailed example of the usual +@c case where CVSUMASK is 007, the developers are all +@c in a group, and that group owns stuff in the +@c repository. Need to talk about group ownership of +@c newly-created directories/files (on some unices, +@c such as SunOS4, setting the setgid bit on the +@c directories will make files inherit the directory's +@c group. On other unices, your mileage may vary. I +@c can't remember what POSIX says about this, if +@c anything). + +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 +@c There is also a larger (largely separate) issue +@c about the meaning of CVSUMASK in a non-unix context. +@c For example, whether there is +@c an equivalent which fits better into other +@c protection schemes like POSIX.6, VMS, &c. +@c +@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 +@c interact? +@c +@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"). + +Using pserver, you will generally need stricter +permissions on the @sc{cvsroot} directory and +directories above it in the tree; see @ref{Password +authentication security}. + +@cindex Setuid +@cindex Setgid +@cindex Security, setuid +@cindex Installed images (VMS) +Some operating systems have features which allow a +particular program to run with the ability to perform +operations which the caller of the program could not. +For example, the set user ID (setuid) or set group ID +(setgid) features of unix or the installed image +feature of VMS. @sc{cvs} was not written to use such +features and therefore attempting to install @sc{cvs} in +this fashion will provide protection against only +accidental lapses; anyone who is trying to circumvent +the measure will be able to do so, and depending on how +you have set it up may gain access to more than just +@sc{cvs}. You may wish to instead consider pserver. It +shares some of the same attributes, in terms of +possibly providing a false sense of security or opening +security holes wider than the ones you are trying to +fix, so read the documentation on pserver security +carefully if you are considering this option +(@ref{Password authentication security}). + +@node Windows permissions +@subsection File Permission issues specific to Windows +@cindex Windows, and permissions +@cindex File permissions, Windows-specific +@cindex Permissions, Windows-specific + +Some file permission issues are specific to Windows +operating systems (Windows 95, Windows NT, and +presumably future operating systems in this family. +Some of the following might apply to OS/2 but I'm not +sure). + +If you are using local @sc{cvs} and the repository is on a +networked file system which is served by the Samba SMB +server, some people have reported problems with +permissions. Enabling WRITE=YES in the samba +configuration is said to fix/workaround it. +Disclaimer: I haven't investigated enough to know the +implications of enabling that option, nor do I know +whether there is something which @sc{cvs} could be doing +differently in order to avoid the problem. If you find +something out, please let us know as described in +@ref{BUGS}. + +@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 + +@noindent +but if it goes in the attic, it would be in + +@example +/usr/local/cvsroot/yoyodyne/tc/Attic/backend.c,v +@end example + +@noindent +@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 CVS in repository +@subsection The CVS directory in the repository +@cindex CVS directory, in repository + +The @file{CVS} directory in each repository directory +contains information such as file attributes (in a file +called @file{CVS/fileattr}. In the +future additional files may be added to this directory, +so implementations should silently ignore additional +files. + +This behavior is implemented only by @sc{cvs} 1.7 and +later; for details see @ref{Watches Compatibility}. + +The format of the @file{fileattr} file is a series of entries +of the following form (where @samp{@{} and @samp{@}} +means the text between the braces can be repeated zero +or more times): + +@var{ent-type} @var{filename} @var{attrname} = @var{attrval} + @{; @var{attrname} = @var{attrval}@} + +@var{ent-type} is @samp{F} for a file, in which case the entry specifies the +attributes for that file. + +@var{ent-type} is @samp{D}, +and @var{filename} empty, to specify default attributes +to be used for newly added files. + +Other @var{ent-type} are reserved for future expansion. @sc{cvs} 1.9 and older +will delete them any time it writes file attributes. +@sc{cvs} 1.10 and later will preserve them. + +Note that the order of the lines is not significant; +a program writing the fileattr file may +rearrange them at its convenience. + +There is currently no way of quoting tabs or line feeds in the +filename, @samp{=} in @var{attrname}, +@samp{;} in @var{attrval}, etc. Note: some implementations also +don't handle a NUL character in any of the fields, but +implementations are encouraged to allow it. + +By convention, @var{attrname} starting with @samp{_} is for an attribute given +special meaning by @sc{cvs}; other @var{attrname}s are for user-defined attributes +(or will be, once implementations start supporting user-defined attributes). + +Built-in attributes: + +@table @code +@item _watched +Present means the file is watched and should be checked out +read-only. + +@item _watchers +Users with watches for this file. Value is +@var{watcher} > @var{type} @{ , @var{watcher} > @var{type} @} +where @var{watcher} is a username, and @var{type} +is zero or more of edit,unedit,commit separated by +@samp{+} (that is, nothing if none; there is no "none" or "all" keyword). + +@item _editors +Users editing this file. Value is +@var{editor} > @var{val} @{ , @var{editor} > @var{val} @} +where @var{editor} is a username, and @var{val} is +@var{time}+@var{hostname}+@var{pathname}, where +@var{time} is when the @code{cvs edit} command (or +equivalent) happened, +and @var{hostname} and @var{pathname} are for the working directory. +@end table + +Example: + +@c FIXME: sanity.sh should contain a similar test case +@c so we can compare this example from something from +@c Real Life(TM). See cvsclient.texi (under Notify) for more +@c discussion of the date format of _editors. +@example +Ffile1 _watched=;_watchers=joe>edit,mary>commit +Ffile2 _watched=;_editors=sue>8 Jan 1975+workstn1+/home/sue/cvs +D _watched= +@end example + +@noindent +means that the file @file{file1} should be checked out +read-only. Furthermore, joe is watching for edits and +mary is watching for commits. The file @file{file2} +should be checked out read-only; sue started editing it +on 8 Jan 1975 in the directory @file{/home/sue/cvs} on +the machine @code{workstn1}. Future files which are +added should be checked out read-only. To represent +this example here, we have shown a space after +@samp{D}, @samp{Ffile1}, and @samp{Ffile2}, but in fact +there must be a single tab character there and no spaces. + +@node Locks +@subsection CVS locks in the repository + +@cindex #cvs.rfl, technical details +@cindex #cvs.pfl, technical details +@cindex #cvs.wfl, technical details +@cindex #cvs.lock, technical details +@cindex Locks, cvs, technical details +For an introduction to @sc{cvs} locks focusing on +user-visible behavior, see @ref{Concurrency}. The +following section is aimed at people who are writing +tools which want to access a @sc{cvs} repository without +interfering with other tools accessing the same +repository. If you find yourself confused by concepts +described here, like @dfn{read lock}, @dfn{write lock}, +and @dfn{deadlock}, you might consult the literature on +operating systems or databases. + +@cindex #cvs.tfl +Any file in the repository with a name starting +with @file{#cvs.rfl.} is a read lock. Any file in +the repository with a name starting with +@file{#cvs.pfl} is a promotable read lock. Any file in +the repository with a name starting with +@file{#cvs.wfl} is a write lock. Old versions of @sc{cvs} +(before @sc{cvs} 1.5) also created files with names starting +with @file{#cvs.tfl}, but they are not discussed here. +The directory @file{#cvs.lock} serves as a master +lock. That is, one must obtain this lock first before +creating any of the other locks. + +To obtain a read lock, first create the @file{#cvs.lock} +directory. This operation must be atomic (which should +be true for creating a directory under most operating +systems). If it fails because the directory already +existed, wait for a while and try again. After +obtaining the @file{#cvs.lock} lock, create a file +whose name is @file{#cvs.rfl.} followed by information +of your choice (for example, hostname and process +identification number). Then remove the +@file{#cvs.lock} directory to release the master lock. +Then proceed with reading the repository. When you are +done, remove the @file{#cvs.rfl} file to release the +read lock. + +Promotable read locks are a concept you may not find in other literature on +concurrency. They are used to allow a two (or more) pass process to only lock +a file for read on the first (read) pass(es), then upgrade its read locks to +write locks if necessary for a final pass, still assured that the files have +not changed since they were first read. @sc{cvs} uses promotable read locks, +for example, to prevent commit and tag verification passes from interfering +with other reading processes. It can then lock only a single directory at a +time for write during the write pass. + +To obtain a promotable read lock, first create the @file{#cvs.lock} directory, +as with a non-promotable read lock. Then check +that there are no files that start with +@file{#cvs.pfl}. If there are, remove the master @file{#cvs.lock} directory, +wait awhile (CVS waits 30 seconds between lock attempts), and try again. If +there are no other promotable locks, go ahead and create a file whose name is +@file{#cvs.pfl} followed by information of your choice (for example, CVS uses +its hostname and the process identification number of the CVS server process +creating the lock). If versions of @sc{cvs} older than version 1.12.4 access +your repository directly (not via a @sc{cvs} server of version 1.12.4 or +later), then you should also create a read lock since older versions of CVS +will ignore the promotable lock when attempting to create their own write lock. +Then remove the master @file{#cvs.lock} directory in order to allow other +processes to obtain read locks. + +To obtain a write lock, first create the +@file{#cvs.lock} directory, as with read locks. Then +check that there are no files whose names start with +@file{#cvs.rfl.} and no files whose names start with @file{#cvs.pfl} that are +not owned by the process attempting to get the write lock. If either exist, +remove @file{#cvs.lock}, wait for a while, and try again. If +there are no readers or promotable locks from other processes, then create a +file whose name is @file{#cvs.wfl} followed by information of your choice +(again, CVS uses the hostname and server process identification +number). Remove your @file{#cvs.pfl} file if present. Hang on to the +@file{#cvs.lock} lock. Proceed +with writing the repository. When you are done, first +remove the @file{#cvs.wfl} file and then the +@file{#cvs.lock} directory. Note that unlike the +@file{#cvs.rfl} file, the @file{#cvs.wfl} file is just +informational; it has no effect on the locking operation +beyond what is provided by holding on to the +@file{#cvs.lock} lock itself. + +Note that each lock (write lock or read lock) only locks +a single directory in the repository, including +@file{Attic} and @file{CVS} but not including +subdirectories which represent other directories under +version control. To lock an entire tree, you need to +lock each directory (note that if you fail to obtain +any lock you need, you must release the whole tree +before waiting and trying again, to avoid deadlocks). + +Note also that @sc{cvs} expects write locks to control +access to individual @file{foo,v} files. @sc{rcs} has +a scheme where the @file{,foo,} file serves as a lock, +but @sc{cvs} does not implement it and so taking out a +@sc{cvs} write lock is recommended. See the comments at +rcs_internal_lockfile in the @sc{cvs} source code for +further discussion/rationale. + +@node CVSROOT storage +@subsection How files are stored in the CVSROOT directory +@cindex CVSROOT, storage of files + +The @file{$CVSROOT/CVSROOT} directory contains the +various administrative files. In some ways this +directory is just like any other directory in the +repository; it contains @sc{rcs} files whose names end +in @samp{,v}, and many of the @sc{cvs} commands operate +on it the same way. However, there are a few +differences. + +For each administrative file, in addition to the +@sc{rcs} file, there is also a checked out copy of the +file. For example, there is an @sc{rcs} file +@file{loginfo,v} and a file @file{loginfo} which +contains the latest revision contained in +@file{loginfo,v}. When you check in an administrative +file, @sc{cvs} should print + +@example +cvs commit: Rebuilding administrative file database +@end example + +@noindent +and update the checked out copy in +@file{$CVSROOT/CVSROOT}. If it does not, there is +something wrong (@pxref{BUGS}). To add your own files +to the files to be updated in this fashion, you can add +them to the @file{checkoutlist} administrative file +(@pxref{checkoutlist}). + +@cindex modules.db +@cindex modules.pag +@cindex modules.dir +By default, the @file{modules} file behaves as +described above. If the modules file is very large, +storing it as a flat text file may make looking up +modules slow (I'm not sure whether this is as much of a +concern now as when @sc{cvs} first evolved this +feature; I haven't seen benchmarks). Therefore, by +making appropriate edits to the @sc{cvs} source code +one can store the modules file in a database which +implements the @code{ndbm} interface, such as Berkeley +db or GDBM. If this option is in use, then the modules +database will be stored in the files @file{modules.db}, +@file{modules.pag}, and/or @file{modules.dir}. +@c I think fileattr also will use the database stuff. +@c Anything else? + +For information on the meaning of the various +administrative files, see @ref{Administrative files}. + +@node Working directory storage +@section How data is stored in the working directory + +@c FIXME: Somewhere we should discuss timestamps (test +@c case "stamps" in sanity.sh). But not here. Maybe +@c in some kind of "working directory" chapter which +@c would encompass the "Builds" one? But I'm not sure +@c whether that is a good organization (is it based on +@c what the user wants to do?). + +@cindex CVS directory, in 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. + +The files are stored according to the text file +convention for the system in question. This means that +working directories are not portable between systems +with differing conventions for storing text files. +This is intentional, on the theory that the files being +managed by @sc{cvs} probably will not be portable between +such systems either. + +@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. It can +be either an absolute pathname or a relative pathname; +@sc{cvs} has had the ability to read either format +since at least version 1.3 or so. The relative +pathname is relative to the root, and is the more +sensible approach, but the absolute pathname is quite +common and implementations should accept either. For +example, after the command + +@example +cvs -d :local:/usr/local/cvsroot checkout yoyodyne/tc +@end example + +@noindent +@file{Root} will contain + +@example +:local:/usr/local/cvsroot +@end example + +@noindent +and @file{Repository} will contain either + +@example +/usr/local/cvsroot/yoyodyne/tc +@end example + +@noindent +or + +@example +yoyodyne/tc +@end example + +If the particular working directory does not correspond +to a directory in the repository, then @file{Repository} +should contain @file{CVSROOT/Emptydir}. +@cindex Emptydir, in CVSROOT directory +@cindex CVSROOT/Emptydir directory + +@cindex Entries file, in CVS directory +@cindex CVS/Entries file +@item Entries +This file lists the files and directories in the +working directory. +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 + +@noindent +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 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}. +If there was a conflict, @var{conflict} can be set to +the modification time of the file after the file has been +written with conflict markers (@pxref{Conflicts example}). +Thus if @var{conflict} is subsequently 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). + +The timezone on the timestamp in CVS/Entries (local or +universal) should be the same as the operating system +stores for the timestamp of the file itself. For +example, on Unix the file's timestamp is in universal +time (UT), so the timestamp in CVS/Entries should be +too. On @sc{vms}, the file's timestamp is in local +time, so @sc{cvs} on @sc{vms} should use local time. +This rule is so that files do not appear to be modified +merely because the timezone changed (for example, to or +from summer time). +@c See comments and calls to gmtime() and friends in +@c src/vers_ts.c (function time_stamp). + +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 + +@noindent +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. + +The lines in the @file{Entries} file can be in any order. + +@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). + +Programs which are writing rather than reading can +safely ignore @file{Entries.Log} if they so choose. + +@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}. +@c FIXME: This needs to be better documented, in places +@c other than Working Directory Storage. +@c FIXCVS: The fact that this setting exists needs to +@c be more visible to the user. For example "cvs +@c status foo", in the case where the file would be +@c gotten except for Entries.Static, might say +@c something to distinguish this from other cases. +@c One thing that periodically gets suggested is to +@c have "cvs update" print something when it skips +@c files due to Entries.Static, but IMHO that kind of +@c noise pretty much makes the Entries.Static feature +@c useless. + +@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 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 Baserev file, in CVS directory +@cindex CVS/Baserev file +@item Baserev +The file lists the revision for each of the files in +the @file{Base} directory. The format is: + +@example +B@var{name}/@var{rev}/@var{expansion} +@end example + +@noindent +where @var{expansion} should be ignored, to allow for +future expansion. + +@cindex Baserev.tmp file, in CVS directory +@cindex CVS/Baserev.tmp file +@item Baserev.tmp +This file is to @file{Baserev} as @file{Entries.Backup} +is to @file{Entries}. That is, to write @file{Baserev}, +first write the new contents to @file{Baserev.tmp} and +then (atomically where possible), rename it to +@file{Baserev}. + +@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 +@cindex Administrative files (intro) +@cindex Modules file +@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 +some commands work better when at least the +@file{modules} file is properly set up. + +The most important of these files is the @file{modules} +file. It defines all modules in the repository. This +is a sample @file{modules} file. + +@c FIXME: The CVSROOT line is a goofy example now that +@c mkmodules doesn't exist. +@example +CVSROOT CVSROOT +modules CVSROOT modules +cvs gnu/cvs +rcs gnu/rcs +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 four lines in the example +above are examples of such lines. + +@c FIXME: might want to introduce the concept of options in modules file +@c (the old example which was here, -i mkmodules, is obsolete). + +The line that defines the module called @samp{modules} +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 + +You edit the administrative files in the same way that you would edit +any other module. Use @samp{cvs checkout CVSROOT} to get a working +copy, edit it, and commit your changes in the normal way. + +It is possible to commit an erroneous administrative +file. You can often fix the error and check in a new +revision, but sometimes a particularly bad error in the +administrative file makes it impossible to commit new +revisions. +@c @xref{Bad administrative files} for a hint +@c about how to solve such situations. +@c -- administrative file checking-- + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Multiple repositories +@section Multiple repositories +@cindex Multiple repositories +@cindex Repositories, multiple +@cindex Many repositories +@cindex Parallel repositories +@cindex Disjoint repositories +@cindex CVSROOT, multiple repositories + +In some situations it is a good idea to have more than +one repository, for instance if you have two +development groups that work on separate projects +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 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. With @sc{cvs} +version 1.10, a single command cannot recurse into +directories from different repositories. With development +versions of @sc{cvs}, you can check out code from multiple +servers into your working directory. @sc{cvs} will +recurse and handle all the details of making +connections to as many server machines as necessary to +perform the requested command. Here is an example of +how to set up a working directory: + +@example +cvs -d server1:/cvs co dir1 +cd dir1 +cvs -d server2:/root co sdir +cvs update +@end example + +The @code{cvs co} commands set up the working +directory, and then the @code{cvs update} command will +contact server2, to update the dir1/sdir subdirectory, +and server1, to update everything else. + +@c FIXME: Does the FAQ have more about this? I have a +@c dim recollection, but I'm too lazy to check right now. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Creating a repository +@section Creating a repository + +@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, so most machines should be +adequate. For details see @ref{Server requirements}. +@c Possible that we should be providing a quick rule of +@c thumb, like the 32M memory for the server. That +@c might increase the number of people who are happy +@c with the answer, without following the xref. + +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 @sc{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). + +The repository should be accessible +(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. + +@cindex Locks, cvs, and backups +@cindex #cvs.rfl, and backups +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 + +@node Moving a repository +@section Moving a repository +@cindex Repository, moving +@cindex Moving a repository +@cindex Copying a repository + +Just as backing up the files in the repository is +pretty much like backing up any other files, if you +need to move a repository from one place to another it +is also pretty much like just moving any other +collection of files. + +The main thing to consider is that working directories +point to the repository. The simplest way to deal with +a moved repository is to just get a fresh working +directory after the move. Of course, you'll want to +make sure that the old working directory had been +checked in before the move, or you figured out some +other way to make sure that you don't lose any +changes. If you really do want to reuse the existing +working directory, it should be possible with manual +surgery on the @file{CVS/Repository} files. You can +see @ref{Working directory storage}, for information on +the @file{CVS/Repository} and @file{CVS/Root} files, but +unless you are sure you want to bother, it probably +isn't worth it. +@c FIXME: Surgery on CVS/Repository should be avoided +@c by making RELATIVE_REPOS the default. +@c FIXME-maybe: might want some documented way to +@c change the CVS/Root files in some particular tree. +@c But then again, I don't know, maybe just having +@c people do this in perl/shell/&c isn't so bad... + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Remote repositories +@section Remote repositories +@cindex Repositories, remote +@cindex Remote repositories +@cindex Client/Server Operation +@cindex Server, CVS +@cindex Remote repositories, port specification +@cindex Repositories, remote, port specification +@cindex Client/Server Operation, port specification +@cindex pserver (client/server connection method), port specification +@cindex kserver (client/server connection method), port specification +@cindex gserver (client/server connection method), port specification +@cindex port, specifying for remote repositories + + Your working copy of the sources can be on a +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 +[:@var{method}:][[@var{user}][:@var{password}]@@]@var{hostname}[:[@var{port}]]/path/to/repository +@end example + +Specifying a password in the repository name is not recommended during +checkout, since this will cause @sc{cvs} to store a cleartext copy of the +password in each created directory. @code{cvs login} first instead +(@pxref{Password authentication client}). + +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 does not start with a @samp{/}, then the default is @code{ext} +or @code{server}, depending on your platform; both are +described in @ref{Connecting via rsh}. + +@cindex connection method options +@cindex options, connection method +@cindex proxies, web, using +@cindex web proxies, using +The @code{gserver} and @code{pserver} connection +methods all accept optional method options, specified as part of the +@var{method} string, like so: + +@example +:@var{method}[;@var{option}=@var{arg}...]: +@end example + +Currently, the only two valid connection options are @code{proxy}, which +takes a hostname as an argument, and @code{proxyport}, which takes a port +number as an argument. These options can be used to connect via an HTTP +tunnel style web proxy. For example, to connect pserver via a web proxy +at www.myproxy.net and port 8000, you would use a method of: + +@example +:pserver;proxy=www.myproxy.net;proxyport=8000: +@end example + +@strong{NOTE: The rest of the connection string is required to connect to +the server as noted in the upcoming sections on password authentication, +gserver and kserver. The example above would only modify the @var{method} +portion of the repository name.} + +@cindex CVS_PROXY_PORT +@var{proxy} must be supplied to connect to a CVS server via a +proxy server, but @var{proxyport} will default to port 8080 if not supplied. +@var{PROXYPORT} may also be set via the @var{CVS_PROXY_PORT} environment +variable. +@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:. +@c Also note that rsh not pserver is the right choice if you want +@c users to be able to create their own repositories +@c (because of the --allow-root related issues). +@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 +* GSSAPI authenticated:: Direct connections using GSSAPI +* Kerberos authenticated:: Direct connections with Kerberos +* Connecting via fork:: Using a forked @code{cvs server} to connect +@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. +Estimating the known areas of large memory consumption +should be sufficient to estimate memory requirements. +There are two such areas documented here; other memory +consumption should be small by comparison (if you find +that is not the case, let us know, as described in +@ref{BUGS}, so we can update this documentation). + +The first area of big memory consumption is large +checkouts, when using the @sc{cvs} server. The 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. + +@c What about disk usage in /tmp on the server? I think that +@c it can be substantial, but I haven't looked at this +@c again and tried to figure it out ("cvs import" is +@c probably the worst case...). + +The second area of large memory consumption is +@code{diff}, when checking in large files. This is +required even for binary files. The rule of thumb is +to allow about ten times the size of the largest file +you will want to check in, although five times may be +adequate. For example, if you want to check in a file +which is 10 megabytes, you should have 100 megabytes of +memory on the machine doing the checkin (the server +machine for client/server, or the machine running +@sc{cvs} for non-client/server). This can be swap +space rather than physical memory. Because the memory +is only required briefly, there is no particular need +to allow memory for more than one such checkin at a +time. +@c The 5-10 times rule of thumb is from Paul Eggert for +@c GNU diff. I don't think it is in the GNU diff +@c manual or anyplace like that. +@c +@c Probably we could be saying more about +@c non-client/server CVS. +@c I would guess for non-client/server CVS in an NFS +@c environment the biggest issues are the network and +@c the NFS server. + +Resource consumption for the client is even more +modest---any machine with enough capacity to run the +operating system in question should have little +trouble. +@c Is that true? I think the client still wants to +@c (bogusly) store entire files in memory at times. + +For information on disk space requirements, see +@ref{Creating a repository}. + +@node Connecting via rsh +@subsection Connecting with rsh + +@cindex rsh +@sc{cvs} uses the @samp{rsh} protocol to perform these +operations, so the remote user host needs to have a +@file{.rhosts} file which grants access to the local +user. Note that the program that @sc{cvs} uses for this +purpose may be specified using the @file{--with-rsh} +flag to configure. + +For example, suppose you are the user @samp{mozart} on +the local machine @samp{toe.example.com}, and the +server machine is @samp{faun.example.org}. On +faun, put the following line into the file +@file{.rhosts} in @samp{bach}'s home directory: + +@example +toe.example.com mozart +@end example + +@noindent +Then test that @samp{rsh} is working with + +@example +rsh -l bach faun.example.org 'echo $PATH' +@end example + +@cindex CVS_SERVER, environment variable +Next you have to make sure that @code{rsh} will be able +to find the server. Make sure that the path which +@code{rsh} printed in the above example includes the +directory containing a program named @code{cvs} which +is the server. You need to set the path in +@file{.bashrc}, @file{.cshrc}, etc., not @file{.login} +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 @file{inetd.conf} or start a +@sc{cvs} server daemon. + +@cindex :server:, setting up +@cindex :ext:, setting up +@cindex Kerberos, using kerberized rsh +@cindex SSH (rsh replacement) +@cindex rsh replacements (Kerberized, SSH, &c) +There are two access methods that you use in @code{CVSROOT} +for rsh. @code{:server:} specifies an internal rsh +client, which is supported only by some @sc{cvs} ports. +@code{:ext:} specifies an external rsh program. By +default this is @code{rsh} (unless otherwise specified +by the @file{--with-rsh} flag to configure) 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 @sc{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{faun.example.org}, you are ready to go: + +@example +cvs -d :ext:bach@@faun.example.org:/usr/local/cvsroot checkout foo +@end example + +@noindent +(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 + +The @sc{cvs} client can also connect to the server +using a password protocol. This is particularly useful +if using @code{rsh} is not feasible (for example, +the server is behind a firewall), and Kerberos also is +not available. + + To use this method, it is necessary to make +some adjustments on both the server and client sides. + +@menu +* Password authentication server:: Setting up the server +* Password authentication client:: Using the client +* Password authentication security:: What this method does and does not do +@end menu + +@node Password authentication server +@subsubsection Setting up the server for password authentication + +First of all, you probably want to tighten the +permissions on the @file{$CVSROOT} and +@file{$CVSROOT/CVSROOT} directories. See @ref{Password +authentication security}, for more details. + +@cindex pserver (subcommand) +@cindex Remote repositories, port specification +@cindex Repositories, remote, port specification +@cindex Client/Server Operation, port specification +@cindex pserver (client/server connection method), port specification +@cindex kserver (client/server connection method), port specification +@cindex gserver (client/server connection method), port specification +@cindex port, specifying for remote repositories +@cindex Password server, setting up +@cindex Authenticating server, setting up +@cindex inetd, configuring for pserver +@cindex xinetd, configuring for pserver +@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 +connection on the right port. By default, the port +number is 2401; it would be different if your client +were compiled with @code{CVS_AUTH_PORT} defined to +something else, though. This can also be specified in the CVSROOT variable +(@pxref{Remote repositories}) or overridden with the CVS_CLIENT_PORT +environment variable (@pxref{Environment variables}). + + If your @code{inetd} allows raw port numbers in +@file{/etc/inetd.conf}, then the following (all on a +single line in @file{inetd.conf}) should be sufficient: + +@example +2401 stream tcp nowait root /usr/local/bin/cvs +cvs -f --allow-root=/usr/cvsroot pserver +@end example + +@noindent +(You could also use the +@samp{-T} option to specify a temporary directory.) + +The @samp{--allow-root} option specifies the allowable +@sc{cvsroot} directory. Clients which attempt to use a +different @sc{cvsroot} directory will not be allowed to +connect. If there is more than one @sc{cvsroot} +directory which you want to allow, repeat the option. +(Unfortunately, many versions of @code{inetd} have very small +limits on the number of arguments and/or the total length +of the command. The usual solution to this problem is +to have @code{inetd} run a shell script which then invokes +@sc{cvs} with the necessary arguments.) + + If your @code{inetd} wants a symbolic service +name instead of a raw port number, then put this in +@file{/etc/services}: + +@example +cvspserver 2401/tcp +@end example + +@noindent +and put @code{cvspserver} instead of @code{2401} in @file{inetd.conf}. + +If your system uses @code{xinetd} instead of @code{inetd}, +the procedure is slightly different. +Create a file called @file{/etc/xinetd.d/cvspserver} containing the following: + +@example +service cvspserver +@{ + port = 2401 + socket_type = stream + protocol = tcp + wait = no + user = root + passenv = PATH + server = /usr/local/bin/cvs + server_args = -f --allow-root=/usr/cvsroot pserver +@} +@end example + +@noindent +(If @code{cvspserver} is defined in @file{/etc/services}, you can omit +the @code{port} line.) + + Once the above is taken care of, restart your +@code{inetd}, or do whatever is necessary to force it +to reread its initialization files. + +If you are having trouble setting this up, see +@ref{Connection}. + +@cindex CVS 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 +file is generally used, so people don't compromise +their regular passwords when they access the +repository. This file is +@file{$CVSROOT/CVSROOT/passwd} (@pxref{Intro +administrative files}). It uses a colon-separated +format, similar to @file{/etc/passwd} on Unix systems, +except that it has fewer fields: @sc{cvs} username, +optional password, and an optional system username for +@sc{cvs} to run as if authentication succeeds. Here is +an example @file{passwd} file with five entries: + +@example +anonymous: +bach:ULtgRLXo7NRxs +spwang:1sOp854gDF3DY +melissa:tGX1fS8sun6rY:pubcvs +qproj:XR4EZcEs0szik:pubcvs +@end example + +@noindent +(The passwords are encrypted according to the standard +Unix @code{crypt()} function, so it is possible to +paste in passwords directly from regular Unix +@file{/etc/passwd} files.) + +The first line in the example will grant access to any +@sc{cvs} client attempting to authenticate as user +@code{anonymous}, no matter what password they use, +including an empty password. (This is typical for +sites granting anonymous read-only access; for +information on how to do the "read-only" part, see +@ref{Read-only access}.) + +The second and third lines will grant access to +@code{bach} and @code{spwang} if they supply their +respective plaintext passwords. + +@cindex User aliases +The fourth line will grant access to @code{melissa}, if +she supplies the correct password, but her @sc{cvs} +operations will actually run on the server side under +the system user @code{pubcvs}. Thus, there need not be +any system user named @code{melissa}, but there +@emph{must} be one named @code{pubcvs}. + +The fifth line shows that system user identities can be +shared: any client who successfully authenticates as +@code{qproj} will actually run as @code{pubcvs}, just +as @code{melissa} does. That way you could create a +single, shared system user for each project in your +repository, and give each developer their own line in +the @file{$CVSROOT/CVSROOT/passwd} file. The @sc{cvs} +username on each line would be different, but the +system username would be the same. The reason to have +different @sc{cvs} usernames is that @sc{cvs} will log their +actions under those names: when @code{melissa} commits +a change to a project, the checkin is recorded in the +project's history under the name @code{melissa}, not +@code{pubcvs}. And the reason to have them share a +system username is so that you can arrange permissions +in the relevant area of the repository such that only +that account has write-permission there. + +If the system-user field is present, all +password-authenticated @sc{cvs} commands run as that +user; if no system user is specified, @sc{cvs} simply +takes the @sc{cvs} username as the system username and +runs commands as that user. In either case, if there +is no such user on the system, then the @sc{cvs} +operation will fail (regardless of whether the client +supplied a valid password). + +The password and system-user fields can both be omitted +(and if the system-user field is omitted, then also +omit the colon that would have separated it from the +encrypted password). For example, this would be a +valid @file{$CVSROOT/CVSROOT/passwd} file: + +@example +anonymous::pubcvs +fish:rKa5jzULzmhOo:kfogel +sussman:1sOp854gDF3DY +@end example + +@noindent +When the password field is omitted or empty, then the +client's authentication attempt will succeed with any +password, including the empty string. However, the +colon after the @sc{cvs} username is always necessary, +even if the password is empty. + +@sc{cvs} can also fall back to use system authentication. +When authenticating a password, the server first checks +for the user in the @file{$CVSROOT/CVSROOT/passwd} +file. If it finds the user, it will use that entry for +authentication as described above. But if it does not +find the user, or if the @sc{cvs} @file{passwd} file +does not exist, then the server can try to authenticate +the username and password using the operating system's +user-lookup routines (this "fallback" behavior can be +disabled by setting @code{SystemAuth=no} in the +@sc{cvs} @file{config} file, @pxref{config}). + +The default fallback behavior is to look in +@file{/etc/passwd} for this system password unless your +system has PAM (Pluggable Authentication Modules) +and your @sc{cvs} server executable was configured to +use it at compile time (using @code{./configure --enable-pam} - see the +INSTALL file for more). In this case, PAM will be consulted instead. +This means that @sc{cvs} can be configured to use any password +authentication source PAM can be configured to use (possibilities +include a simple UNIX password, NIS, LDAP, and others) in its +global configuration file (usually @file{/etc/pam.conf} +or possibly @file{/etc/pam.d/cvs}). See your PAM documentation +for more details on PAM configuration. + +Note that PAM is an experimental feature in @sc{cvs} and feedback is +encouraged. Please send a mail to one of the @sc{cvs} mailing lists +(@code{info-cvs@@gnu.org} or @code{bug-cvs@@gnu.org}) if you use the +@sc{cvs} PAM support. + +@strong{WARNING: Using PAM gives the system administrator much more +flexibility about how @sc{cvs} users are authenticated but +no more security than other methods. See below for more.} + +CVS needs an "auth" and "account" module in the +PAM configuration file. A typical PAM configuration +would therefore have the following lines +in @file{/etc/pam.conf} to emulate the standard @sc{cvs} +system @file{/etc/passwd} authentication: + +@example +cvs auth required pam_unix.so +cvs account required pam_unix.so +@end example + +The the equivalent @file{/etc/pam.d/cvs} would contain + +@example +auth required pam_unix.so +account required pam_unix.so +@end example + +Some systems require a full path to the module so that +@file{pam_unix.so} (Linux) would become something like +@file{/usr/lib/security/$ISA/pam_unix.so.1} (Sun Solaris). +See the @file{contrib/pam} subdirectory of the @sc{cvs} +source distribution for further example configurations. + +The PAM service name given above as "cvs" is just +the service name in the default configuration and can be +set using +@code{./configure --with-hardcoded-pam-service-name=} +before compiling. @sc{cvs} can also be configured to use whatever +name it is invoked as as its PAM service name using +@code{./configure --without-hardcoded-pam-service-name}, but this +feature should not be used if you may not have control of the name +@sc{cvs} will be invoked as. + +Be aware, also, that falling back to system +authentication might be a security risk: @sc{cvs} +operations would then be authenticated with that user's +regular login password, and the password flies across +the network in plaintext. See @ref{Password +authentication security} for more on this. +This may be more of a problem with PAM authentication +because it is likely that the source of the system +password is some central authentication service like +LDAP which is also used to authenticate other services. + +On the other hand, PAM makes it very easy to change your password +regularly. If they are given the option of a one-password system for +all of their activities, users are often more willing to change their +password on a regular basis. + +In the non-PAM configuration where the password is stored in the +@file{CVSROOT/passwd} file, it is difficult to change passwords on a +regular basis since only administrative users (or in some cases +processes that act as an administrative user) are typically given +access to modify this file. Either there needs to be some +hand-crafted web page or set-uid program to update the file, or the +update needs to be done by submitting a request to an administrator to +perform the duty by hand. In the first case, having to remember to +update a separate password on a periodic basis can be difficult. In +the second case, the manual nature of the change will typically mean +that the password will not be changed unless it is absolutely +necessary. + +Note that PAM administrators should probably avoid configuring +one-time-passwords (OTP) for @sc{cvs} authentication/authorization. If +OTPs are desired, the administrator may wish to encourage the use of +one of the other Client/Server access methods. See the section on +@pxref{Remote repositories} for a list of other methods. + +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. + +Unlike many of the files in @file{$CVSROOT/CVSROOT}, it +is normal to edit the @file{passwd} file in-place, +rather than via @sc{cvs}. This is because of the +possible security risks of having the @file{passwd} +file checked out to people's working copies. If you do +want to include the @file{passwd} file in checkouts of +@file{$CVSROOT/CVSROOT}, see @ref{checkoutlist}. + +@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?" +@c Also note that htpasswd, at least the version I had, +@c likes to clobber the third field. + +@node Password authentication client +@subsubsection Using the client with password authentication +@cindex Login (subcommand) +@cindex Password client, using +@cindex Authenticated client, using +@cindex :pserver:, setting up +To run a @sc{cvs} command on a remote repository via +the password-authenticating server, one specifies the +@code{pserver} protocol, optional username, repository host, an +optional port number, and path to the repository. For example: + +@example +cvs -d :pserver:faun.example.org:/usr/local/cvsroot checkout someproj +@end example + +@noindent +or + +@example +CVSROOT=:pserver:bach@@faun.example.org:2401/usr/local/cvsroot +cvs checkout someproj +@end example + +However, unless you're connecting to a public-access +repository (i.e., one where that username doesn't +require a password), you'll need to supply a password or @dfn{log in} first. +Logging in verifies your password with the repository and stores it in a file. +It's done with the @code{login} command, which will +prompt you interactively for the password if you didn't supply one as part of +@var{$CVSROOT}: + +@example +cvs -d :pserver:bach@@faun.example.org:/usr/local/cvsroot login +CVS password: +@end example + +@noindent +or + +@example +cvs -d :pserver:bach:p4ss30rd@@faun.example.org:/usr/local/cvsroot login +@end example + +After you enter the password, @sc{cvs} verifies it with +the server. If the verification succeeds, then that +combination of username, host, repository, and password +is permanently recorded, so future transactions with +that repository won't require you to run @code{cvs +login}. (If verification fails, @sc{cvs} will exit +complaining that the password was incorrect, and +nothing will be recorded.) + +The records are stored, by default, in the file +@file{$HOME/.cvspass}. That file's format is +human-readable, and to a degree human-editable, but +note that the passwords are not stored in +cleartext---they are trivially encoded to protect them +from "innocent" compromise (i.e., inadvertent viewing +by a system administrator or other non-malicious +person). + +@cindex CVS_PASSFILE, environment variable +You can change the default location of this file by +setting the @code{CVS_PASSFILE} environment variable. +If you use this variable, make sure you set it +@emph{before} @code{cvs login} is run. If you 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. + +Once you have logged in, all @sc{cvs} commands using +that remote repository and username will authenticate +with the stored password. So, for example + +@example +cvs -d :pserver:bach@@faun.example.org:/usr/local/cvsroot checkout foo +@end example + +@noindent +should just work (unless the password changes on the +server side, in which case you'll have to re-run +@code{cvs login}). + +Note that if the @samp{:pserver:} were not present in +the repository specification, @sc{cvs} would assume it +should use @code{rsh} to connect with the server +instead (@pxref{Connecting via rsh}). + +Of course, once you have a working copy checked out and +are running @sc{cvs} commands from within it, there is +no longer any need to specify the repository +explicitly, because @sc{cvs} can deduce the repository +from the working copy's @file{CVS} subdirectory. + +@c FIXME: seems to me this needs somewhat more +@c explanation. +@cindex Logout (subcommand) +The password for a given remote repository can be +removed from the @code{CVS_PASSFILE} by using the +@code{cvs logout} command. + +@node Password authentication security +@subsubsection Security considerations with password authentication + +@cindex Security, of pserver +The passwords are stored on the client side in a +trivial encoding of the cleartext, and transmitted in +the same encoding. The encoding is done only to +prevent inadvertent password compromises (i.e., a +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 +for login access. On the other hand, once a user has +non-read-only +access to the repository, she can execute programs on +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). + +Note that because the @file{$CVSROOT/CVSROOT} directory +contains @file{passwd} and other files which are used +to check security, you must control the permissions on +this directory as tightly as the permissions on +@file{/etc}. The same applies to the @file{$CVSROOT} +directory itself and any directory +above it in the tree. Anyone who has write access to +such a directory will have the ability to become any +user on the system. Note that these permissions are +typically tighter than you would use if you are not +using pserver. +@c TODO: Would be really nice to document/implement a +@c scheme where the CVS server can run as some non-root +@c user, e.g. "cvs". CVSROOT/passwd would contain a +@c bunch of entries of the form foo:xxx:cvs (or the "cvs" +@c would be implicit). This would greatly reduce +@c security risks such as those hinted at in the +@c previous paragraph. I think minor changes to CVS +@c might be required but mostly this would just need +@c someone who wants to play with it, document it, &c. + +In summary, anyone who gets the password gets +repository access (which may imply some measure of general system +access as well). The password is available to anyone +who can sniff network packets or read a protected +(i.e., user read-only) file. If you want real +security, get Kerberos. + +@node GSSAPI authenticated +@subsection Direct connection with GSSAPI + +@cindex GSSAPI +@cindex Security, GSSAPI +@cindex :gserver:, setting up +@cindex Kerberos, using :gserver: +GSSAPI is a generic interface to network security +systems such as Kerberos 5. +If you have a working GSSAPI library, you can have +@sc{cvs} connect via a direct @sc{tcp} connection, +authenticating with GSSAPI. + +To do this, @sc{cvs} needs to be compiled with GSSAPI +support; when configuring @sc{cvs} it tries to detect +whether GSSAPI libraries using Kerberos version 5 are +present. You can also use the @file{--with-gssapi} +flag to configure. + +The connection is authenticated using GSSAPI, but the +message stream is @emph{not} authenticated by default. +You must use the @code{-a} global option to request +stream authentication. + +The data transmitted is @emph{not} encrypted by +default. Encryption support must be compiled into both +the client and the server; use the +@file{--enable-encrypt} configure option to turn it on. +You must then use the @code{-x} global option to +request encryption. + +GSSAPI connections are handled on the server side by +the same server which handles the password +authentication server; see @ref{Password authentication +server}. If you are using a GSSAPI mechanism such as +Kerberos which provides for strong authentication, you +will probably want to disable the ability to +authenticate via cleartext passwords. To do so, create +an empty @file{CVSROOT/passwd} password file, and set +@code{SystemAuth=no} in the config file +(@pxref{config}). + +The GSSAPI server uses a principal name of +cvs/@var{hostname}, where @var{hostname} is the +canonical name of the server host. You will have to +set this up as required by your GSSAPI mechanism. + +To connect using GSSAPI, use the @samp{:gserver:} method. For +example, + +@example +cvs -d :gserver:faun.example.org:/usr/local/cvsroot checkout foo +@end example + +@node Kerberos authenticated +@subsection Direct connection with Kerberos + +@cindex Kerberos, using :kserver: +@cindex Security, Kerberos +@cindex :kserver:, setting up +The easiest way to use Kerberos is to use the Kerberos +@code{rsh}, as described in @ref{Connecting via rsh}. +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. + +This section concerns the Kerberos network security +system, version 4. Kerberos version 5 is supported via +the GSSAPI generic network security interface, as +described in the previous section. + +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. + +The CVS client will attempt to connect to port 1999 by default. + +@cindex kinit +When you want to use @sc{cvs}, get a ticket in the +usual way (generally @code{kinit}); it must be a ticket +which allows you to log into the server machine. Then +you are ready to go: + +@example +cvs -d :kserver:faun.example.org:/usr/local/cvsroot checkout foo +@end example + +Previous versions of @sc{cvs} would fall back to a +connection via rsh; this version will not do so. + +@node Connecting via fork +@subsection Connecting with fork + +@cindex fork, access method +@cindex :fork:, setting up +This access method allows you to connect to a +repository on your local disk via the remote protocol. +In other words it does pretty much the same thing as +@code{:local:}, but various quirks, bugs and the like are +those of the remote @sc{cvs} rather than the local +@sc{cvs}. + +For day-to-day operations you might prefer either +@code{:local:} or @code{:fork:}, depending on your +preferences. Of course @code{:fork:} comes in +particularly handy in testing or +debugging @code{cvs} and the remote protocol. +Specifically, we avoid all of the network-related +setup/configuration, timeouts, and authentication +inherent in the other remote access methods but still +create a connection which uses the remote protocol. + +To connect using the @code{fork} method, use +@samp{:fork:} and the pathname to your local +repository. For example: + +@example +cvs -d :fork:/usr/local/cvsroot checkout foo +@end example + +@cindex CVS_SERVER, and :fork: +As with @code{:ext:}, the server is called @samp{cvs} +by default, or the value of the @code{CVS_SERVER} +environment variable. + +@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}). + +Unlike with previous versions of @sc{cvs}, read-only +users should be able merely to read the repository, and +not to execute programs on the server or otherwise gain +unexpected levels of access. Or to be more accurate, +the @emph{known} holes have been plugged. Because this +feature is new and has not received a comprehensive +security audit, you should use whatever level of +caution seems warranted given your attitude concerning +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 + +@noindent + (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{-T} global option (@pxref{Global options}), +the @code{TMPDIR} environment variable (@pxref{Environment variables}), +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 +@chapter Starting a project with CVS +@cindex Starting a project with CVS +@cindex Creating a project + +@comment --moduledb-- +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. + +@menu +* Setting up the files:: Getting the files into the repository +* Defining the module:: How to make a module of the files +@end menu +@c -- File permissions! + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Setting up the files +@section Setting up the files + +The first step is to create the files inside the repository. This can +be done in a couple of different ways. + +@c -- The contributed scripts +@menu +* From files:: This method is useful with old projects + where files already exists. +* From other version control systems:: Old projects where you want to + preserve history from another system. +* From scratch:: Creating a directory tree from scratch. +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node From 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 +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{wdir}}, and you want them to appear in the +repository as @file{$CVSROOT/yoyodyne/@var{rdir}}, you can do this: + +@example +$ 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} +flag, @sc{cvs} starts an editor and prompts for a +message. The string @samp{yoyo} is a @dfn{vendor tag}, +and @samp{start} is a @dfn{release tag}. They may fill +no purpose in this context, but since @sc{cvs} requires +them they must be present. @xref{Tracking sources}, for +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 .. +$ cvs checkout yoyodyne/@var{rdir} # @r{Explanation below} +$ diff -r @var{wdir} yoyodyne/@var{rdir} +$ rm -r @var{wdir} +@end example + +@noindent +Erasing the original sources is a good idea, to make sure that you do +not accidentally edit them in @var{wdir}, bypassing @sc{cvs}. +Of course, it would be wise to make sure that you have +a backup of the sources before you remove them. + +The @code{checkout} command can either take a module +name as argument (as it has done in all previous +examples) or a path name relative to @code{$CVSROOT}, +as it did in the example above. + +It is a good idea to check that the permissions +@sc{cvs} sets on the directories inside @code{$CVSROOT} +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 +@subsection Creating Files From Other Version Control Systems +@cindex Importing files, from other version control systems + +If you have a project which you are maintaining with +another version control system, such as @sc{rcs}, you +may wish to put the files from that project into +@sc{cvs}, and preserve the revision history of the +files. + +@table @asis +@cindex RCS, importing files from +@item From RCS +If you have been using @sc{rcs}, find the @sc{rcs} +files---usually a file named @file{foo.c} will have its +@sc{rcs} file in @file{RCS/foo.c,v} (but it could be +other places; consult the @sc{rcs} documentation for +details). Then create the appropriate directories in +@sc{cvs} if they do not already exist. Then copy the +files into the appropriate directories in the @sc{cvs} +repository (the name in the repository must be the name +of the source file with @samp{,v} added; the files go +directly in the appropriate directory of the repository, +not in an @file{RCS} subdirectory). This is one of the +few times when it is a good idea to access the @sc{cvs} +repository directly, rather than using @sc{cvs} +commands. Then you are ready to check out a new +working directory. +@c Someday there probably should be a "cvs import -t +@c rcs" or some such. It could even create magic +@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 +@sc{rcs} files in the standard format. If yours does, +export the @sc{rcs} files and then follow the above +instructions. + +Failing that, probably your best bet is to write a +script that will check out the files one revision at a +time using the command line interface to the other +system, and then check the revisions into @sc{cvs}. +The @file{sccs2rcs} script mentioned below may be a +useful example to follow. + +@cindex SCCS, importing files from +@item From SCCS +There is a script in the @file{contrib} directory of +the @sc{cvs} source distribution called @file{sccs2rcs} +which converts @sc{sccs} files to @sc{rcs} files. +Note: you must run it on a machine which has both +@sc{sccs} and @sc{rcs} installed, and like everything +else in contrib it is unsupported (your mileage may +vary). + +@cindex PVCS, importing files from +@item From PVCS +There is a script in the @file{contrib} directory of +the @sc{cvs} source distribution called @file{pvcs_to_rcs} +which converts @sc{pvcs} archives to @sc{rcs} files. +You must run it on a machine which has both +@sc{pvcs} and @sc{rcs} installed, and like everything +else in contrib it is unsupported (your mileage may +vary). See the comments in the script for details. +@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 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: + +@example +$ mkdir tc +$ mkdir tc/man +$ mkdir tc/testing +@end example + +After that, you use the @code{import} command to create +the corresponding (empty) directory structure inside +the repository: + +@example +$ cd tc +$ cvs import -m "Created directory structure" yoyodyne/@var{dir} yoyo start +@end example + +This will add yoyodyne/@var{dir} as a directory under +@code{$CVSROOT}. + +Then, use @code{add} to add files (and new directories) +as they appear. + +Check that the permissions @sc{cvs} sets on the +directories inside @code{$CVSROOT} are reasonable. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Defining the module +@section Defining the module +@cindex Defining a module +@cindex Editing the modules file +@cindex Module, defining +@cindex Modules file, changing + +The next step is to define the module in the +@file{modules} file. This is not strictly necessary, +but modules can be convenient in grouping together +related files and directories. + +In simple cases these steps are sufficient to define a module. + +@enumerate +@item +Get a working copy of the modules file. + +@example +$ cvs checkout CVSROOT/modules +$ cd CVSROOT +@end example + +@item +Edit the file and insert a line that defines the module. @xref{Intro +administrative files}, for an introduction. @xref{modules}, for a full +description of the modules file. You can use the +following line to define the module @samp{tc}: + +@example +tc yoyodyne/tc +@end example + +@item +Commit your changes to the modules file. + +@example +$ cvs commit -m "Added the tc module." modules +@end example + +@item +Release the modules module. + +@example +$ cd .. +$ cvs release -d CVSROOT +@end example +@end enumerate + +@c --------------------------------------------------------------------- +@node Revisions +@chapter Revisions + +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. + +@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 +* Tagging the working directory:: The cvs tag command +* Tagging by date/tag:: The cvs rtag command +* Modifying tags:: Adding, renaming, and deleting tags +* Tagging add/remove:: Tags with adding and removing files +* Sticky tags:: Certain tags are persistent +@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 + +It is also possible to end up with numbers containing +more than one period, for example @samp{1.3.2.2}. Such +revisions represent revisions on branches +(@pxref{Branching and merging}); such revision numbers +are explained in detail in @ref{Branches and +revisions}. + +@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. +@c +@c Well, in the context of software release numbers, +@c "major" and "minor" release or version numbers are +@c documented in at least the GNU Coding Standards, but I'm +@c still not sure I find that a valid reason to apply the +@c terminology to RCS revision numbers. "First", "Second", +@c "subsequent", and so on is almost surely clearer, +@c especially to a novice reader. -DRP +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}. +(When using client/server @sc{cvs}, +only files that are actually sent to the server are considered.) + +@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{Branching and merging}). + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Tags +@section Tags--Symbolic revisions +@cindex Tags + +The revision numbers live a life of their own. They +need not have anything at all to do with the release +numbers of your software product. Depending +on how you use @sc{cvs} the revision numbers might change several times +between two releases. As an example, some of the +source files that make up @sc{rcs} 5.6 have the following +revision numbers: +@cindex RCS revision numbers + +@example +ci.c 5.21 +co.c 5.9 +ident.c 5.3 +rcs.c 5.12 +rcsbase.h 5.11 +rcsdiff.c 5.10 +rcsedit.c 5.11 +rcsfcmp.c 5.9 +rcsgen.c 5.10 +rcslex.c 5.11 +rcsmap.c 5.2 +rcsutil.c 5.10 +@end example + +@cindex tag (subcommand), introduction +@cindex Tags, symbolic name +@cindex Symbolic name (tag) +@cindex Name, symbolic (tag) +@cindex HEAD, as reserved tag name +@cindex BASE, as reserved tag name +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 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 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 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 @sc{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 +@file{taginfo} file (@pxref{taginfo}). +@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 Tags, example +The following example shows how you can add a tag to a +file. The commands must be issued inside your working +directory. That is, you should issue the +command in the directory where @file{backend.c} +resides. + +@example +$ cvs tag rel-0-4 backend.c +T backend.c +$ 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 /u/cvsroot/yoyodyne/tc/backend.c,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + + Existing Tags: + rel-0-4 (revision: 1.4) + +@end example + +For a complete summary of the syntax of @code{cvs tag}, +including the various options, see @ref{Invoking CVS}. + +There is seldom reason to tag a file in isolation. A more common use is +to tag all the files that constitute a module with the same tag at +strategic points in the development life-cycle, such as when a release +is made. + +@example +$ cvs tag rel-1-0 . +cvs tag: Tagging . +T Makefile +T backend.c +T driver.c +T frontend.c +T parser.c +@end example + +@noindent +(When you give @sc{cvs} a directory as argument, it generally applies the +operation to all the files in that directory, and (recursively), to any +subdirectories that it may contain. @xref{Recursive behavior}.) + +@cindex Retrieving an old revision using tags +@cindex Tags, retrieving old revisions +The @code{checkout} command has a flag, @samp{-r}, that lets you check out +a certain revision of a module. This flag makes it easy to +retrieve the sources that make up release 1.0 of the module @samp{tc} at +any time in the future: + +@example +$ cvs checkout -r rel-1-0 tc +@end example + +@noindent +This is useful, for instance, if someone claims that there is a bug in +that release, but you cannot find the bug in the current working copy. + +You can also check out a module as it was at any given date. +@xref{checkout options}. When specifying @samp{-r} to +any of these commands, you will need beware of sticky +tags; see @ref{Sticky tags}. + +When you tag more than one file with the same tag you +can think about the tag as "a curve drawn through a +matrix of filename vs. revision number." Say we have 5 +files with the following revisions: + +@example +@group + file1 file2 file3 file4 file5 + + 1.1 1.1 1.1 1.1 /--1.1* <-*- TAG + 1.2*- 1.2 1.2 -1.2*- + 1.3 \- 1.3*- 1.3 / 1.3 + 1.4 \ 1.4 / 1.4 + \-1.5*- 1.5 + 1.6 +@end group +@end example + +At some time in the past, the @code{*} versions were tagged. +You can think of the tag as a handle attached to the curve +drawn through the tagged revisions. When you pull on +the handle, you get all the tagged revisions. Another +way to look at it is that you "sight" through a set of +revisions that is "flat" along the tagged revisions, +like this: + +@example +@group + file1 file2 file3 file4 file5 + + 1.1 + 1.2 + 1.1 1.3 _ + 1.1 1.2 1.4 1.1 / + 1.2*----1.3*----1.5*----1.2*----1.1 (--- <--- Look here + 1.3 1.6 1.3 \_ + 1.4 1.4 + 1.5 +@end group +@end example + +@node Tagging the working directory +@section Specifying what to tag from the working directory + +@cindex tag (subcommand) +The example in the previous section demonstrates one of +the most common ways to choose which revisions to tag. +Namely, running the @code{cvs tag} command without +arguments causes @sc{cvs} to select the revisions which +are checked out in the current working directory. For +example, if the copy of @file{backend.c} in working +directory was checked out from revision 1.4, then +@sc{cvs} will tag revision 1.4. Note that the tag is +applied immediately to revision 1.4 in the repository; +tagging is not like modifying a file, or other +operations in which one first modifies the working +directory and then runs @code{cvs commit} to transfer +that modification to the repository. + +One potentially surprising aspect of the fact that +@code{cvs tag} operates on the repository is that you +are tagging the checked-in revisions, which may differ +from locally modified files in your working directory. +If you want to avoid doing this by mistake, specify the +@samp{-c} option to @code{cvs tag}. If there are any +locally modified files, @sc{cvs} will abort with an +error before it tags any files: + +@example +$ cvs tag -c rel-0-4 +cvs tag: backend.c is locally modified +cvs [tag aborted]: correct the above errors first! +@end example + +@node Tagging by date/tag +@section Specifying what to tag by date or revision +@cindex rtag (subcommand) + +The @code{cvs rtag} command tags the repository as of a +certain date or time (or can be used to tag the latest +revision). @code{rtag} works directly on the +repository contents (it requires no prior checkout and +does not look for a working directory). + +The following options specify which date or revision to +tag. See @ref{Common options}, for a complete +description of them. + +@table @code +@item -D @var{date} +Tag the most recent revision no later than @var{date}. + +@item -f +Only useful with the @samp{-D @var{date}} or @samp{-r @var{tag}} +flags. If no matching revision is found, use the most +recent revision (instead of ignoring the file). + +@item -r @var{tag} +Only tag those files that contain existing tag @var{tag}. +@end table + +The @code{cvs tag} command also allows one to specify +files by revision or date, using the same @samp{-r}, +@samp{-D}, and @samp{-f} options. However, this +feature is probably not what you want. The reason is +that @code{cvs tag} chooses which files to tag based on +the files that exist in the working directory, rather +than the files which existed as of the given tag/date. +Therefore, you are generally better off using @code{cvs +rtag}. The exceptions might be cases like: + +@example +cvs tag -r 1.4 stable backend.c +@end example + +@node Modifying tags +@section Deleting, moving, and renaming tags + +@c Also see: +@c "How do I move or rename a magic branch tag?" +@c in the FAQ (I think the issues it talks about still +@c apply, but this could use some sanity.sh work). + +Normally one does not modify tags. They exist in order +to record the history of the repository and so deleting +them or changing their meaning would, generally, not be +what you want. + +However, there might be cases in which one uses a tag +temporarily or accidentally puts one in the wrong +place. Therefore, one might delete, move, or rename a +tag. + +@noindent +@strong{WARNING: the commands in this section are +dangerous; they permanently discard historical +information and it can be difficult or impossible to +recover from errors. If you are a @sc{cvs} +administrator, you may consider restricting these +commands with the @file{taginfo} file (@pxref{taginfo}).} + +@cindex Deleting tags +@cindex Deleting branch tags +@cindex Removing tags +@cindex Removing branch tags +@cindex Tags, deleting +@cindex Branch tags, deleting +To delete a tag, specify the @samp{-d} option to either +@code{cvs tag} or @code{cvs rtag}. For example: + +@example +cvs rtag -d rel-0-4 tc +@end example + +@noindent +deletes the non-branch tag @code{rel-0-4} from the module @code{tc}. +In the event that branch tags are encountered within the repository +with the given name, a warning message will be issued and the branch +tag will not be deleted. If you are absolutely certain you know what +you are doing, the @code{-B} option may be specified to allow deletion +of branch tags. In that case, any non-branch tags encountered will +trigger warnings and will not be deleted. + +@noindent +@strong{WARNING: Moving branch tags is very dangerous! If you think +you need the @code{-B} option, think again and ask your @sc{cvs} +administrator about it (if that isn't you). There is almost certainly +another way to accomplish what you want to accomplish.} + +@cindex Moving tags +@cindex Moving branch tags +@cindex Tags, moving +@cindex Branch tags, moving +When we say @dfn{move} a tag, we mean to make the same +name point to different revisions. For example, the +@code{stable} tag may currently point to revision 1.4 +of @file{backend.c} and perhaps we want to make it +point to revision 1.6. To move a non-branch tag, specify the +@samp{-F} option to either @code{cvs tag} or @code{cvs +rtag}. For example, the task just mentioned might be +accomplished as: + +@example +cvs tag -r 1.6 -F stable backend.c +@end example + +@noindent +If any branch tags are encountered in the repository +with the given name, a warning is issued and the branch +tag is not disturbed. If you are absolutely certain you +wish to move the branch tag, the @code{-B} option may be specified. +In that case, non-branch tags encountered with the given +name are ignored with a warning message. + +@noindent +@strong{WARNING: Moving branch tags is very dangerous! If you think you +need the @code{-B} option, think again and ask your @sc{cvs} +administrator about it (if that isn't you). There is almost certainly +another way to accomplish what you want to accomplish.} + +@cindex Renaming tags +@cindex Tags, renaming +When we say @dfn{rename} a tag, we mean to make a +different name point to the same revisions as the old +tag. For example, one may have misspelled the tag name +and want to correct it (hopefully before others are +relying on the old spelling). To rename a tag, first +create a new tag using the @samp{-r} option to +@code{cvs rtag}, and then delete the old name. (Caution: +this method will not work with branch tags.) +This leaves the new tag on exactly the +same files as the old tag. For example: + +@example +cvs rtag -r old-name-0-4 rel-0-4 tc +cvs rtag -d old-name-0-4 tc +@end example + +@node Tagging add/remove +@section Tagging and adding and removing files + +The subject of exactly how tagging interacts with +adding and removing files is somewhat obscure; for the +most part @sc{cvs} will keep track of whether files +exist or not without too much fussing. By default, +tags are applied to only files which have a revision +corresponding to what is being tagged. Files which did +not exist yet, or which were already removed, simply +omit the tag, and @sc{cvs} knows to treat the absence +of a tag as meaning that the file didn't exist as of +that tag. + +However, this can lose a small amount of information. +For example, suppose a file was added and then removed. +Then, if the tag is missing for that file, there is no +way to know whether the tag refers to the time before +the file was added, or the time after it was removed. +If you specify the @samp{-r} option to @code{cvs rtag}, +then @sc{cvs} tags the files which have been removed, +and thereby avoids this problem. For example, one +might specify @code{-r HEAD} to tag the head. + +On the subject of adding and removing files, the +@code{cvs rtag} command has a @samp{-a} option which +means to clear the tag from removed files that would +not otherwise be tagged. For example, one might +specify this option in conjunction with @samp{-F} when +moving a tag. If one moved a tag without @samp{-a}, +then the tag in the removed files might still refer to +the old revision, rather than reflecting the fact that +the file had been removed. I don't think this is +necessary if @samp{-r} is specified, as noted above. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Sticky tags +@section Sticky tags +@cindex Sticky tags +@cindex Tags, sticky + +@c A somewhat related issue is per-directory sticky +@c tags (see comment at CVS/Tag in node Working +@c directory storage); we probably want to say +@c something like "you can set a sticky tag for only +@c some files, but you don't want to" or some such. + +Sometimes a working copy's revision has extra data +associated with it, for example it might be on a branch +(@pxref{Branching and merging}), or restricted to +versions prior to a certain date by @samp{checkout -D} +or @samp{update -D}. Because this data persists -- +that is, it applies to subsequent commands in the +working copy -- we refer to it as @dfn{sticky}. + +Most of the time, stickiness is an obscure aspect of +@sc{cvs} that you don't need to think about. However, +even if you don't want to use the feature, you may need +to know @emph{something} about sticky tags (for +example, how to avoid them!). + +You can use the @code{status} command to see if any +sticky tags or dates are set: + +@example +$ cvs status 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 /u/cvsroot/yoyodyne/tc/driver.c,v + Sticky Tag: rel-1-0-patches (branch: 1.7.2) + Sticky Date: (none) + Sticky Options: (none) + +@end example + +@cindex Resetting sticky tags +@cindex Sticky tags, resetting +@cindex Deleting sticky tags +The sticky tags will remain on your working files until +you delete them with @samp{cvs update -A}. The +@samp{-A} option merges local changes into the version of the +file from the head of the trunk, removing any sticky tags, +dates, or options. See @ref{update} for more on the operation +of @code{cvs update}. + +@cindex Sticky date +The most common use of sticky tags is to identify which +branch one is working on, as described in +@ref{Accessing branches}. However, non-branch +sticky tags have uses as well. 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} +commands 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. + +People often want to retrieve an old version of +a file without setting a sticky tag. This can +be done with the @samp{-p} option to @code{checkout} or +@code{update}, which sends the contents of the file to +standard output. For example: +@example +$ cvs update -p -r 1.1 file1 >file1 +=================================================================== +Checking out file1 +RCS: /tmp/cvs-sanity/cvsroot/first-dir/Attic/file1,v +VERS: 1.1 +*************** +$ +@end example + +However, this isn't the easiest way, if you are asking +how to undo a previous checkin (in this example, put +@file{file1} back to the way it was as of revision +1.1). In that case you are better off using the +@samp{-j} option to @code{update}; for further +discussion see @ref{Merging two revisions}. + +@c --------------------------------------------------------------------- +@node Branching and merging +@chapter Branching and merging +@cindex Branching +@cindex Merging +@cindex Copying changes +@cindex Main trunk and branches +@cindex Revision tree, making branches +@cindex Branches, copying changes between +@cindex Changes, copying between branches +@cindex Modifications, copying between branches + +@sc{cvs} allows you to isolate changes onto a separate +line of development, known as a @dfn{branch}. When you +change files on a branch, those changes do not appear +on the main trunk or other branches. + +Later you can move changes from one branch to another +branch (or the main trunk) by @dfn{merging}. Merging +involves first running @code{cvs update -j}, to merge +the changes into the working directory. +You can then commit that revision, and thus effectively +copy the changes onto another branch. + +@menu +* Branches motivation:: What branches are good for +* Creating a branch:: Creating a branch +* Accessing branches:: Checking out and updating branches +* Branches and revisions:: Branches are reflected in revision numbers +* Magic branch numbers:: Magic branch numbers +* 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? +* Merging and keywords:: Avoiding conflicts due to keyword substitution +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Branches motivation +@section What branches are good for +@cindex Branches motivation +@cindex What branches are good for +@cindex Motivation for branches + +@c FIXME: this node mentions one way to use branches, +@c but it is by no means the only way. For example, +@c the technique of committing a new feature on a branch, +@c until it is ready for the main trunk. The whole +@c thing is generally speaking more akin to the +@c "Revision management" node although it isn't clear to +@c me whether policy matters should be centralized or +@c distributed throughout the relevant sections. +Suppose that release 1.0 of tc has been made. You are continuing to +develop tc, planning to create release 1.1 in a couple of months. After a +while your customers start to complain about a fatal bug. You check +out release 1.0 (@pxref{Tags}) and find the bug +(which turns out to have a trivial fix). However, the current revision +of the sources are in a state of flux and are not expected to be stable +for at least another month. There is no way to make a +bug fix release based on the newest sources. + +The thing to do in a situation like this is to create a @dfn{branch} on +the revision trees for all the files that make up +release 1.0 of tc. You can then make +modifications to the branch without disturbing the main trunk. When the +modifications are finished you can elect to either incorporate them on +the main trunk, or leave them on the branch. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Creating a branch +@section Creating a branch +@cindex Creating a branch +@cindex Branch, creating a +@cindex tag (subcommand), creating a branch using +@cindex rtag (subcommand), creating a branch using + +You can create a branch with @code{tag -b}; for +example, assuming you're in a working copy: + +@example +$ cvs tag -b rel-1-0-patches +@end example + +@c FIXME: we should be more explicit about the value of +@c having a tag on the branchpoint. For example +@c "cvs tag rel-1-0-patches-branchpoint" before +@c the "cvs tag -b". This points out that +@c rel-1-0-patches is a pretty awkward name for +@c this example (more so than for the rtag example +@c below). + +This splits off a branch based on the current revisions +in the working copy, assigning that branch the name +@samp{rel-1-0-patches}. + +It is important to understand that branches get created +in the repository, not in the working copy. Creating a +branch based on current revisions, as the above example +does, will @emph{not} automatically switch the working +copy to be on the new branch. For information on how +to do that, see @ref{Accessing branches}. + +You can also create a branch without reference to any +working copy, by using @code{rtag}: + +@example +$ cvs rtag -b -r rel-1-0 rel-1-0-patches tc +@end example + +@samp{-r rel-1-0} says that this branch should be +rooted at the revision that +corresponds to the tag @samp{rel-1-0}. It need not +be the most recent revision -- it's often useful to +split a branch off an old revision (for example, when +fixing a bug in a past release otherwise known to be +stable). + +As with @samp{tag}, the @samp{-b} flag tells +@code{rtag} to create a branch (rather than just a +symbolic revision name). Note that the numeric +revision number that matches @samp{rel-1-0} will +probably be different from file to file. + +So, the full effect of the command is to create a new +branch -- named @samp{rel-1-0-patches} -- in module +@samp{tc}, rooted in the revision tree at the point tagged +by @samp{rel-1-0}. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Accessing branches +@section Accessing branches +@cindex Check out a branch +@cindex Retrieve a branch +@cindex Access a branch +@cindex Identifying a branch +@cindex Branch, check out +@cindex Branch, retrieving +@cindex Branch, accessing +@cindex Branch, identifying + +You can retrieve a branch in one of two ways: by +checking it out fresh from the repository, or by +switching an existing working copy over to the branch. + +To check out a branch from the repository, invoke +@samp{checkout} with the @samp{-r} flag, followed by +the tag name of the branch (@pxref{Creating a branch}): + +@example +$ cvs checkout -r rel-1-0-patches tc +@end example + +Or, if you already have a working copy, you can switch +it to a given branch with @samp{update -r}: + +@example +$ cvs update -r rel-1-0-patches tc +@end example + +@noindent +or equivalently: + +@example +$ cd tc +$ cvs update -r rel-1-0-patches +@end example + +It does not matter if the working copy was originally +on the main trunk or on some other branch -- the above +command will switch it to the named branch. And +similarly to a regular @samp{update} command, +@samp{update -r} merges any changes you have made, +notifying you of conflicts where they occur. + +Once you have a working copy tied to a particular +branch, it remains there until you tell it otherwise. +This means that changes checked in from the working +copy will add new revisions on that branch, while +leaving the main trunk and other branches unaffected. + +@cindex Branches, sticky +To find out what branch a working copy is on, you can +use the @samp{status} command. In its output, look for +the field named @samp{Sticky tag} (@pxref{Sticky tags}) +-- that's @sc{cvs}'s way of telling you the branch, if +any, of the current working files: + +@example +$ 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 /u/cvsroot/yoyodyne/tc/driver.c,v + Sticky Tag: rel-1-0-patches (branch: 1.7.2) + Sticky Date: (none) + Sticky Options: (none) + + Existing Tags: + rel-1-0-patches (branch: 1.7.2) + rel-1-0 (revision: 1.7) + +=================================================================== +File: backend.c Status: Up-to-date + + Version: 1.4 Tue Dec 1 14:39:01 1992 + RCS Version: 1.4 /u/cvsroot/yoyodyne/tc/backend.c,v + Sticky Tag: rel-1-0-patches (branch: 1.4.2) + Sticky Date: (none) + Sticky Options: (none) + + Existing Tags: + rel-1-0-patches (branch: 1.4.2) + rel-1-0 (revision: 1.4) + rel-0-4 (revision: 1.4) + +@end example + +Don't be confused by the fact that the branch numbers +for each file are different (@samp{1.7.2} and +@samp{1.4.2} respectively). The branch tag is the +same, @samp{rel-1-0-patches}, and the files are +indeed on the same branch. The numbers simply reflect +the point in each file's revision history at which the +branch was made. In the above example, one can deduce +that @samp{driver.c} had been through more changes than +@samp{backend.c} before this branch was created. + +See @ref{Branches and revisions} for details about how +branch numbers are constructed. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Branches and revisions +@section Branches and revisions +@cindex Branch number +@cindex Number, branch +@cindex Revision numbers (branches) + +Ordinarily, a file's revision history is a linear +series of increments (@pxref{Revision numbers}): + +@example + +-----+ +-----+ +-----+ +-----+ +-----+ + ! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! + +-----+ +-----+ +-----+ +-----+ +-----+ +@end example + +However, @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 +@c This example used to have a 1.2.2.4 revision, which +@c might help clarify that development can continue on +@c 1.2.2. Might be worth reinstating if it can be done +@c without overfull hboxes. +@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 Magic branch numbers +@section Magic branch numbers + +@c Want xref to here from "log"? + +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.4 becomes +1.2.0.4, 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 a branch +@section Merging an entire branch +@cindex Merging a branch +@cindex -j (merging branches) + +You can merge changes made on a branch into your working copy by giving +the @samp{-j @var{branchname}} flag to the @code{update} subcommand. With one +@samp{-j @var{branchname}} option it merges the changes made between the +greatest common ancestor (GCA) of the branch and the destination revision (in +the simple case below the GCA is the point where the branch forked) and the +newest revision on that branch into your working copy. + +@cindex Join +The @samp{-j} stands for ``join''. + +@cindex Branch merge example +@cindex Example, branch merge +@cindex Merge, branch example +Consider this revision tree: + +@example ++-----+ +-----+ +-----+ +-----+ +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 ! <- The main trunk ++-----+ +-----+ +-----+ +-----+ + ! + ! + ! +---------+ +---------+ +Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 ! + +---------+ +---------+ +@end example + +@noindent +The branch 1.2.2 has been given the tag (symbolic name) @samp{R1fix}. The +following example assumes that the module @samp{mod} contains only one +file, @file{m.c}. + +@example +$ cvs checkout mod # @r{Retrieve the latest revision, 1.4} + +$ cvs update -j R1fix m.c # @r{Merge all changes made on the branch,} + # @r{i.e. the changes between revision 1.2} + # @r{and 1.2.2.2, into your working copy} + # @r{of the file.} + +$ cvs commit -m "Included R1fix" # @r{Create revision 1.5.} +@end example + +A conflict can result from a merge operation. If that +happens, you should resolve it before committing the +new revision. @xref{Conflicts example}. + +If your source files contain keywords (@pxref{Keyword substitution}), +you might be getting more conflicts than strictly necessary. See +@ref{Merging and keywords}, for information on how to avoid this. + +The @code{checkout} command also supports the @samp{-j @var{branchname}} flag. The +same effect as above could be achieved with this: + +@example +$ cvs checkout -j R1fix mod +$ cvs commit -m "Included R1fix" +@end example + +It should be noted that @code{update -j @var{tagname}} will also work but may +not produce the desired result. @xref{Merging adds and removals}, for more. + +@node Merging more than once +@section Merging from a branch several times + +Continuing our example, the revision tree now looks +like this: + +@example ++-----+ +-----+ +-----+ +-----+ +-----+ +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk ++-----+ +-----+ +-----+ +-----+ +-----+ + ! * + ! * + ! +---------+ +---------+ +Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 ! + +---------+ +---------+ +@end example + +@noindent +where the starred line represents the merge from the +@samp{R1fix} branch to the main trunk, as just +discussed. + +Now suppose that development continues on the +@samp{R1fix} branch: + +@example ++-----+ +-----+ +-----+ +-----+ +-----+ +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk ++-----+ +-----+ +-----+ +-----+ +-----+ + ! * + ! * + ! +---------+ +---------+ +---------+ +Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 ! + +---------+ +---------+ +---------+ +@end example + +@noindent +and then you want to merge those new changes onto the +main trunk. If you just use the @code{cvs update -j +R1fix m.c} command again, @sc{cvs} will attempt to +merge again the changes which you have already merged, +which can have undesirable side effects. + +So instead you need to specify that you only want to +merge the changes on the branch which have not yet been +merged into the trunk. To do that you specify two +@samp{-j} options, and @sc{cvs} merges the changes from +the first revision to the second revision. For +example, in this case the simplest way would be + +@example +cvs update -j 1.2.2.2 -j R1fix m.c # @r{Merge changes from 1.2.2.2 to the} + # @r{head of the R1fix branch} +@end example + +The problem with this is that you need to specify the +1.2.2.2 revision manually. A slightly better approach +might be to use the date the last merge was done: + +@example +cvs update -j R1fix:yesterday -j R1fix m.c +@end example + +Better yet, tag the R1fix branch after every merge into +the trunk, and then use that tag for subsequent merges: + +@example +cvs update -j merged_from_R1fix_to_trunk -j R1fix m.c +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Merging two revisions +@section Merging differences between any two revisions +@cindex Merging two revisions +@cindex Revisions, merging differences between +@cindex Differences, merging + +With two @samp{-j @var{revision}} flags, the @code{update} +(and @code{checkout}) command can merge the differences +between any two revisions into your working file. + +@cindex Undoing a change +@cindex Removing a change +@example +$ cvs update -j 1.5 -j 1.3 backend.c +@end example + +@noindent +will undo all changes made between revision +1.3 and 1.5. Note the order of the revisions! + +If you try to use this option when operating on +multiple files, remember that the numeric revisions will +probably be very different between the various files. +You almost always use symbolic +tags rather than revision numbers when operating on +multiple files. + +@cindex Restoring old version of removed file +@cindex Resurrecting old version of dead file +Specifying two @samp{-j} options can also undo file +removals or additions. For example, suppose you have +a file +named @file{file1} which existed as revision 1.1, and +you then removed it (thus adding a dead revision 1.2). +Now suppose you want to add it again, with the same +contents it had previously. Here is how to do it: + +@example +$ cvs update -j 1.2 -j 1.1 file1 +U file1 +$ cvs commit -m test +Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1.3; previous revision: 1.2 +done +$ +@end example + +@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 + +After these commands are executed and a @samp{cvs commit} is done, +file @file{a} will be removed and file @file{d} added in the main branch. +@c (which was determined by trying it) + +Note that using a single static tag (@samp{-j @var{tagname}}) +rather than a dynamic tag (@samp{-j @var{branchname}}) to merge +changes from a branch will usually not remove files which were removed on the +branch since @sc{cvs} does not automatically add static tags to dead revisions. +The exception to this rule occurs when +a static tag has been attached to a dead revision manually. Use the branch tag +to merge all changes from the branch or use two static tags as merge endpoints +to be sure that all intended changes are propagated in the merge. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Merging and keywords +@section Merging and keywords +@cindex Merging, and keyword substitution +@cindex Keyword substitution, and merging +@cindex -j (merging branches), and keyword substitution +@cindex -kk, to avoid conflicts during a merge + +If you merge files containing keywords (@pxref{Keyword +substitution}), you will normally get numerous +conflicts during the merge, because the keywords are +expanded differently in the revisions which you are +merging. + +Therefore, you will often want to specify the +@samp{-kk} (@pxref{Substitution modes}) switch to the +merge command line. By substituting just the name of +the keyword, not the expanded value of that keyword, +this option ensures that the revisions which you are +merging will be the same as each other, and avoid +spurious conflicts. + +For example, suppose you have a file like this: + +@example + +---------+ + _! 1.1.2.1 ! <- br1 + / +---------+ + / + / ++-----+ +-----+ +! 1.1 !----! 1.2 ! ++-----+ +-----+ +@end example + +@noindent +and your working directory is currently on the trunk +(revision 1.2). Then you might get the following +results from a merge: + +@example +$ cat file1 +key $@splitrcskeyword{Revision}: 1.2 $ +. . . +$ cvs update -j br1 +U file1 +RCS file: /cvsroot/first-dir/file1,v +retrieving revision 1.1 +retrieving revision 1.1.2.1 +Merging differences between 1.1 and 1.1.2.1 into file1 +rcsmerge: warning: conflicts during merge +$ cat file1 +@asis{}<<<<<<< file1 +key $@splitrcskeyword{Revision}: 1.2 $ +@asis{}======= +key $@splitrcskeyword{Revision}: 1.1.2.1 $ +@asis{}>>>>>>> 1.1.2.1 +. . . +@end example + +What happened was that the merge tried to merge the +differences between 1.1 and 1.1.2.1 into your working +directory. So, since the keyword changed from +@code{Revision: 1.1} to @code{Revision: 1.1.2.1}, +@sc{cvs} tried to merge that change into your working +directory, which conflicted with the fact that your +working directory had contained @code{Revision: 1.2}. + +Here is what happens if you had used @samp{-kk}: + +@example +$ cat file1 +key $@splitrcskeyword{Revision}: 1.2 $ +. . . +$ cvs update -kk -j br1 +U file1 +RCS file: /cvsroot/first-dir/file1,v +retrieving revision 1.1 +retrieving revision 1.1.2.1 +Merging differences between 1.1 and 1.1.2.1 into file1 +$ cat file1 +key $@splitrcskeyword{Revision}$ +. . . +@end example + +What is going on here is that revision 1.1 and 1.1.2.1 +both expand as plain @code{Revision}, and therefore +merging the changes between them into the working +directory need not change anything. Therefore, there +is no conflict. + +@strong{WARNING: In versions of @sc{cvs} prior to 1.12.2, there was a +major problem with using @samp{-kk} on merges. Namely, @samp{-kk} +overrode any default keyword expansion mode set in the archive file in +the repository. This could, unfortunately for some users, cause data +corruption in binary files (with a default keyword expansion mode set +to @samp{-kb}). Therefore, when a repository contained binary files, +conflicts had to be dealt with manually rather than using @samp{-kk} in +a merge command.} + +In @sc{cvs} version 1.12.2 and later, the keyword expansion mode +provided on the command line to any @sc{cvs} command no longer +overrides the @samp{-kb} keyword expansion mode setting for binary +files, though it will still override other default keyword expansion +modes. You can now safely merge using @samp{-kk} to avoid spurious conflicts +on lines containing RCS keywords, even when your repository contains +binary files. + +@c --------------------------------------------------------------------- +@node Recursive behavior +@chapter Recursive behavior +@cindex Recursive (directory descending) +@cindex Directory, descending +@cindex Descending directories +@cindex Subdirectories + +Almost all of the subcommands of @sc{cvs} work +recursively when you specify a directory as an +argument. For instance, consider this directory +structure: + +@example + @code{$HOME} + | + +--@t{tc} + | | + +--@t{CVS} + | (internal @sc{cvs} files) + +--@t{Makefile} + +--@t{backend.c} + +--@t{driver.c} + +--@t{frontend.c} + +--@t{parser.c} + +--@t{man} + | | + | +--@t{CVS} + | | (internal @sc{cvs} files) + | +--@t{tc.1} + | + +--@t{testing} + | + +--@t{CVS} + | (internal @sc{cvs} files) + +--@t{testpgm.t} + +--@t{test2.t} +@end example + +@noindent +If @file{tc} is the current working directory, the +following is true: + +@itemize @bullet +@item +@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 +subdirectories + +@item +@samp{cvs update .} or just @samp{cvs update} updates +all files in the @code{tc} directory +@end itemize + +If no arguments are given to @code{update} it will +update all files in the current working directory and +all its subdirectories. In other words, @file{.} is a +default argument to @code{update}. This is also true +for most of the @sc{cvs} subcommands, not only the +@code{update} command. + +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} +@end example + +@c --------------------------------------------------------------------- +@node Adding and removing +@chapter Adding, removing, and renaming files and directories + +In the course of a project, one will often add new +files. Likewise with removing or renaming, or with +directories. The general concept to keep in mind in +all these cases is that instead of making an +irreversible change you want @sc{cvs} to record the +fact that a change has taken place, just as with +modifying an existing file. The exact mechanisms to do +this in @sc{cvs} vary depending on the situation. + +@menu +* Adding files:: Adding files +* Removing files:: Removing files +* Removing directories:: Removing directories +* Moving files:: Moving and renaming files +* Moving directories:: Moving and renaming directories +@end menu + +@node Adding files +@section Adding files to a directory +@cindex Adding files + +To add a new file to a directory, follow these steps. + +@itemize @bullet +@item +You must have a working copy of the directory. +@xref{Getting the source}. + +@item +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. 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. +@end itemize + +You can also use the @code{add} command to add a new +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 have to expcicitly name files and +directories that you wish to add to the repository. +However, each directory will need to be added +separately before you will be able to add new files +to those directories. + +@example +$ mkdir -p foo/bar +$ cp ~/myfile foo/bar/myfile +$ cvs add foo foo/bar +$ cvs add foo/bar/myfile +@end example + +@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{Branching and merging}). 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 +@section Removing files +@cindex Removing files +@cindex Deleting files + +@c FIXME: this node wants to be split into several +@c smaller nodes. Could make these children of +@c "Adding and removing", probably (death support could +@c be its own section, for example, as could the +@c various bits about undoing mistakes in adding and +@c removing). +Directories change. New files are added, and old files +disappear. Still, you want to be able to retrieve an +exact copy of old releases. + +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}, +for one way to do that. You can also use the +@code{status} or @code{update} command. If you remove +the file without committing your changes, you will of +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 directory. +You can for instance use @code{rm}. + +@item +Use @samp{cvs remove @var{filename}} to tell @sc{cvs} that +you really want to delete the file. + +@item +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 +not on others, or to re-add another file with the same +name later. @sc{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} [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. 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 +$ cvs remove +cvs remove: Removing . +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 + +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 +CVS ja.h oj.c +$ rm oj.c +$ cvs remove oj.c +cvs remove: scheduling oj.c for removal +cvs remove: use 'cvs commit' to remove this file permanently +$ cvs add oj.c +U oj.c +cvs add: oj.c, version 1.1.1.1, resurrected +@end example + +If you realize your mistake before you run the +@code{remove} command you can use @code{update} to +resurrect the file: + +@example +$ rm oj.c +$ cvs update oj.c +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{Branching and merging}). You can +later merge the removals to another branch if you want +(@pxref{Merging adds and removals}). + +@node Removing directories +@section 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. You don't remove the directory +itself; there is no way to do that. +Instead you specify the @samp{-P} option to +@code{cvs update} or @code{cvs checkout}, +which will cause @sc{cvs} to remove empty +directories from working directories. +(Note that @code{cvs export} always removes empty 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}. 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 Moving files +@section Moving and renaming files +@cindex Moving files +@cindex Renaming files +@cindex Files, moving + +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}.). + +The examples below assume that the file @var{old} is renamed to +@var{new}. + +@menu +* Outside:: The normal way to Rename +* Inside:: A tricky, alternative way +* Rename by copying:: Another tricky, alternative way +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Outside +@subsection 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. +@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} +$ cvs remove @var{old} +$ cvs add @var{new} +$ cvs commit -m "Renamed @var{old} to @var{new}" @var{old} @var{new} +@end example + +This is the simplest way to move a file, it is not +error-prone, and it preserves the history of what was +done. Note that to access the history of the file you +must specify the old or the new name, depending on what +portion of the history you are accessing. For example, +@code{cvs log @var{old}} will give the log up until the +time of the rename. + +When @var{new} is committed its revision numbers will +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 +@subsection Moving the history file + +This method is more dangerous, since it involves moving +files inside the repository. Read this entire section +before trying it out! + +@example +$ cd $CVSROOT/@var{dir} +$ mv @var{old},v @var{new},v +@end example + +@noindent +Advantages: + +@itemize @bullet +@item +The log of changes is maintained intact. + +@item +The revision numbers are not affected. +@end itemize + +@noindent +Disadvantages: + +@itemize @bullet +@item +Old releases cannot easily be fetched from the +repository. (The file will show up as @var{new} even +in revisions from the time before it was renamed). + +@item +There is no log information of when the file was renamed. + +@item +Nasty things might happen if someone accesses the history file +while you are moving it. Make sure no one else runs any of the @sc{cvs} +commands while you move it. +@end itemize + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Rename by copying +@subsection Copying the history file + +This way also involves direct modifications to the +repository. It is safe, but not without drawbacks. + +@example +# @r{Copy the @sc{rcs} file inside the repository} +$ cd $CVSROOT/@var{dir} +$ cp @var{old},v @var{new},v +# @r{Remove the old file} +$ cd ~/@var{dir} +$ rm @var{old} +$ 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 non-branch tag names} +$ cvs tag -d @var{tag1} @var{new} +$ cvs tag -d @var{tag2} @var{new} +@dots{} +@end example + +By removing the tags you will be able to check out old +revisions. + +@noindent +Advantages: + +@itemize @bullet +@item +@c FIXME: Is this true about -D now that we have death +@c support? See 5B.3 in the FAQ. +Checking out old revisions works correctly, as long as +you use @samp{-r@var{tag}} and not @samp{-D@var{date}} +to retrieve the revisions. + +@item +The log of changes is maintained intact. + +@item +The revision numbers are not affected. +@end itemize + +@noindent +Disadvantages: + +@itemize @bullet +@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 --------------------------------------------------------------------- +@node Moving directories +@section Moving and renaming directories +@cindex Moving directories +@cindex Renaming directories +@cindex Directories, moving + +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 hack the repository to rename or +delete a directory in the repository, you can do it +like this: + +@enumerate +@item +Inform everyone who has a checked out copy of the directory that the +directory will be renamed. They should commit all +their changes, and remove their working copies, +before you take the steps below. + +@item +Rename the directory inside the repository. + +@example +$ cd $CVSROOT/@var{parent-dir} +$ mv @var{old-dir} @var{new-dir} +@end example + +@item +Fix the @sc{cvs} administrative files, if necessary (for +instance if you renamed an entire module). + +@item +Tell everyone that they can check out again and continue +working. + +@end enumerate + +If someone had a working copy the @sc{cvs} commands will +cease to work for him, until he removes the directory +that disappeared inside the repository. + +It is almost always better to move the files in the +directory instead of moving the directory. If you move the +directory you are unlikely to be able to retrieve old +releases correctly, since they probably depend on the +name of the directories. + +@c --------------------------------------------------------------------- +@node History browsing +@chapter History browsing +@cindex History browsing +@cindex Traceability +@cindex Isolation + +@ignore +@c This is too long for an introduction (goal is +@c one 20x80 character screen), and also mixes up a +@c variety of issues (parallel development, history, +@c maybe even touches on process control). + +@c -- @quote{To lose ones history is to lose ones soul.} +@c -- /// +@c -- ///Those who cannot remember the past are condemned to repeat it. +@c -- /// -- George Santayana +@c -- /// + +@sc{cvs} tries to make it easy for a group of people to work +together. This is done in two ways: + +@itemize @bullet +@item +Isolation---You have your own working copy of the +source. You are not affected by modifications made by +others until you decide to incorporate those changes +(via the @code{update} command---@pxref{update}). + +@item +Traceability---When something has changed, you can +always see @emph{exactly} what changed. +@end itemize + +There are several features of @sc{cvs} that together lead +to traceability: + +@itemize @bullet +@item +Each revision of a file has an accompanying log +message. + +@item +All commits are optionally logged to a central history +database. + +@item +Logging information can be sent to a user-defined +program (@pxref{loginfo}). +@end itemize + +@c -- More text here. + +This chapter should talk about the history file, the +@code{log} command, the usefulness of ChangeLogs +even when you run @sc{cvs}, and things like that. + +@end ignore + +@c kind of lame, in a lot of ways the above text inside +@c the @ignore motivates this chapter better +Once you have used @sc{cvs} to store a version control +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 +* user-defined logging:: User-defined logging +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node log messages +@section Log messages + +@c FIXME: @xref to place where we talk about how to +@c specify message to commit. +Whenever you commit a file you specify a log message. + +@c FIXME: bring the information here, and get rid of or +@c greatly shrink the "log" node. +To look through the log messages which have been +specified for every revision which has been committed, +use the @code{cvs log} command (@pxref{log}). + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node history database +@section The history database + +@c FIXME: bring the information from the history file +@c and history nodes here. Rewrite it to be motivated +@c better (start out by clearly explaining what gets +@c logged in history, for example). +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}). + +Note: you can control what is logged to this file by using the +@samp{LogHistory} keyword in the @file{CVSROOT/config} file +(@pxref{config}). + +@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 +@section User-defined logging + +@c FIXME: probably should centralize this information +@c here, at least to some extent. Maybe by moving the +@c loginfo, etc., nodes here and replacing +@c the "user-defined logging" node with one node for +@c each method. +You can customize @sc{cvs} to log various kinds of +actions, in whatever manner you choose. These +mechanisms operate by executing a script at various +times. The script might append a message to a file +listing the information and the programmer who created +it, or send mail to a group of developers, or, perhaps, +post a message to a particular newsgroup. To log +commits, use the @file{loginfo} file (@pxref{loginfo}), and +to log tagging operations, use the @file{taginfo} file +(@pxref{taginfo}). + +@c FIXME: What is difference between doing it in the +@c modules file and using loginfo/taginfo? Why should +@c user use one or the other? +To log commits, checkouts, exports, and tags, +respectively, you can also use the @samp{-i}, +@samp{-o}, @samp{-e}, and @samp{-t} options in the +modules file. For a more flexible way of giving +notifications to various users, which requires less in +the way of keeping centralized scripts up to date, use +the @code{cvs watch add} command (@pxref{Getting +Notified}); this command is useful even if you are not +using @code{cvs watch on}. + +@c --------------------------------------------------------------------- +@node Binary files +@chapter Handling binary files +@cindex Binary files + +The most common use for @sc{cvs} is to store text +files. With text files, @sc{cvs} can merge revisions, +display the differences between revisions in a +human-visible fashion, and other such operations. +However, if you are willing to give up a few of these +abilities, @sc{cvs} can store binary files. For +example, one might store a web site in @sc{cvs} +including both text files and binary images. + +@menu +* Binary why:: More details on issues with binary files +* Binary howto:: How to store them +@end menu + +@node Binary why +@section The issues with binary files + +While the need to manage binary files may seem obvious +if the files that you customarily work with are binary, +putting them into version control does present some +additional issues. + +One basic function of version control is to show the +differences between two revisions. For example, if +someone else checked in a new version of a file, you +may wish to look at what they changed and determine +whether their changes are good. For text files, +@sc{cvs} provides this functionality via the @code{cvs +diff} command. For binary files, it may be possible to +extract the two revisions and then compare them with a +tool external to @sc{cvs} (for example, word processing +software often has such a feature). If there is no +such tool, one must track changes via other mechanisms, +such as urging people to write good log messages, and +hoping that the changes they actually made were the +changes that they intended to make. + +Another ability of a version control system is the +ability to merge two revisions. For @sc{cvs} this +happens in two contexts. The first is when users make +changes in separate working directories +(@pxref{Multiple developers}). The second is when one +merges explicitly with the @samp{update -j} command +(@pxref{Branching and merging}). + +In the case of text +files, @sc{cvs} can merge changes made independently, +and signal a conflict if the changes conflict. With +binary files, the best that @sc{cvs} can do is present +the two different copies of the file, and leave it to +the user to resolve the conflict. The user may choose +one copy or the other, or may run an external merge +tool which knows about that particular file format, if +one exists. +Note that having the user merge relies primarily on the +user to not accidentally omit some changes, and thus is +potentially error prone. + +If this process is thought to be undesirable, the best +choice may be to avoid merging. To avoid the merges +that result from separate working directories, see the +discussion of reserved checkouts (file locking) in +@ref{Multiple developers}. To avoid the merges +resulting from branches, restrict use of branches. + +@node Binary howto +@section How to store binary files + +There are two issues with using @sc{cvs} to store +binary files. The first is that @sc{cvs} by default +converts line endings between the canonical form in +which they are stored in the repository (linefeed +only), and the form appropriate to the operating system +in use on the client (for example, carriage return +followed by line feed for Windows NT). + +The second is that a binary file might happen to +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. + +Here is an example of how you can create a new file +using the @samp{-kb} flag: + +@example +$ echo '$@splitrcskeyword{Id}$' > kotest +$ cvs add -kb -m"A test file" kotest +$ cvs ci -m"First checkin; contains a keyword" kotest +@end example + +If a file accidentally gets added without @samp{-kb}, +one can use the @code{cvs admin} command to recover. +For example: + +@example +$ echo '$@splitrcskeyword{Id}$' > kotest +$ cvs add -m"A test file" kotest +$ cvs ci -m"First checkin; contains a keyword" kotest +$ cvs admin -kb kotest +$ cvs update -A kotest +# @r{For non-unix systems:} +# @r{Copy in a good copy of the file from outside CVS} +$ cvs commit -m "make it binary" kotest +@end example + +@c Trying to describe this for both unix and non-unix +@c in the same description is very confusing. Might +@c want to split the two, or just ditch the unix "shortcut" +@c (unixheads don't do much with binary files, anyway). +@c This used to say "(Try the above example, and do a +@c @code{cat kotest} after every command)". But that +@c only really makes sense for the unix case. +When you check in the file @file{kotest} the file is +not preserved as a binary file, because you did not +check it in as a binary file. The @code{cvs +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. If you need to +cope with line endings (that is, you are using +@sc{cvs} 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. +On unix, the @code{cvs update -A} command suffices. +@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" +(Note that you can use @code{cvs log} to determine the default keyword +substitution method for a file and @code{cvs status} to determine +the keyword substitution method for a working copy.) + +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 +@c Another, probably better, way to tell is to read the +@c file in text mode, write it to a temp file in text +@c mode, and then do a binary mode compare of the two +@c files. If they differ, it is a binary file. This +@c might have problems on VMS (or some other system +@c with several different text modes), but in general +@c should be relatively portable. The only other +@c downside I can think of is that it would be fairly +@c slow, but that is perhaps a small price to pay for +@c not having your files corrupted. Another issue is +@c what happens if you import a text file with bare +@c linefeeds on Windows. Such files will show up on +@c Windows sometimes (I think some native windows +@c programs even write them, on occasion). Perhaps it +@c is reasonable to treat such files as binary; after +@c all it is something of a presumption to assume that +@c the user would want the linefeeds converted to CRLF. + +@c --------------------------------------------------------------------- +@node Multiple developers +@chapter Multiple developers +@cindex Multiple developers +@cindex Team of developers +@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. 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. + +@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 communication, 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 re-implement 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 @sc{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). + + +One other idea (which could work along with the +existing "cvs admin -l" reserved checkouts, as well as +the above): + +"cvs editors" could show who has the file locked, if +someone does. + +@end ignore + +@menu +* File status:: A file can be in several states +* Updating a file:: Bringing a file up-to-date +* Conflicts example:: An informative example +* 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node File status +@section File status +@cindex File status +@cindex Status of a file + +@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 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 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. + +@item Unresolved Conflict +@cindex Unresolved Conflict +@c FIXCVS - This file status needs to be changed to some more informative +@c text that distinguishes it more clearly from each of the Locally Added, +@c File had conflicts on merge, and Unknown status types, but an exact and +@c succinct wording escapes me at the moment. +A file with the same name as this new file has been added to the repository +from a second workspace. This file will need to be moved out of the way +to allow an @code{update} to complete. + +@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 + +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 FIXME: Working revision can also be something like +@c "-1.3" for a locally removed file. Not at all +@c self-explanatory (and it is possible that CVS should +@c be changed rather than documenting this). + +@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 +@section Bringing a file up to date +@cindex Bringing a file up to date +@cindex Updating a file +@cindex Merging a file +@cindex Update, introduction + +When you want to update or merge a file, use the @code{update} +command. For files that are not up to date this is roughly equivalent +to a @code{checkout} command: the newest revision of the file is +extracted from the repository and put in your working directory. + +Your modifications to a file are never lost when you +use @code{update}. If no newer revision exists, +running @code{update} has no effect. If you have +edited the file, and a newer revision is available, +@sc{cvs} will merge all changes into your working copy. + +For instance, imagine that you checked out revision 1.4 and started +editing it. In the meantime someone else committed revision 1.5, and +shortly after that revision 1.6. If you run @code{update} on the file +now, @sc{cvs} will incorporate all changes between revision 1.4 and 1.6 into +your file. + +@cindex Overlap +If any of the changes between 1.4 and 1.6 were made too +close to any of the changes you have made, an +@dfn{overlap} occurs. In such cases a warning is +printed, and the resulting file includes both +versions of the lines that overlap, delimited by +special markers. +@xref{update}, for a complete description of the +@code{update} command. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Conflicts example +@section Conflicts example +@cindex Merge, an example +@cindex Example of merge +@cindex driver.c (merge example) + +Suppose revision 1.4 of @file{driver.c} contains this: + +@example +#include + +void main() +@{ + parse(); + if (nerr == 0) + gencode(); + else + fprintf(stderr, "No code generated.\n"); + exit(nerr == 0 ? 0 : 1); +@} +@end example + +@noindent +Revision 1.6 of @file{driver.c} contains this: + +@example +#include + +int main(int argc, + char **argv) +@{ + parse(); + if (argc != 1) + @{ + fprintf(stderr, "tc: No args expected.\n"); + exit(1); + @} + if (nerr == 0) + gencode(); + else + fprintf(stderr, "No code generated.\n"); + exit(!!nerr); +@} +@end example + +@noindent +Your working copy of @file{driver.c}, based on revision +1.4, contains this before you run @samp{cvs update}: +@c -- Really include "cvs"? + +@example +#include +#include + +void main() +@{ + init_scanner(); + parse(); + if (nerr == 0) + gencode(); + else + fprintf(stderr, "No code generated.\n"); + exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +@} +@end example + +@noindent +You run @samp{cvs update}: +@c -- Really include "cvs"? + +@example +$ cvs update driver.c +RCS file: /usr/local/cvsroot/yoyodyne/tc/driver.c,v +retrieving revision 1.4 +retrieving revision 1.6 +Merging differences between 1.4 and 1.6 into driver.c +rcsmerge warning: overlaps during merge +cvs update: conflicts found in driver.c +C driver.c +@end example + +@noindent +@cindex Conflicts (merge example) +@sc{cvs} tells you that there were some conflicts. +Your original working file is saved unmodified in +@file{.#driver.c.1.4}. The new version of +@file{driver.c} contains this: + +@example +#include +#include + +int main(int argc, + char **argv) +@{ + init_scanner(); + parse(); + if (argc != 1) + @{ + fprintf(stderr, "tc: No args expected.\n"); + exit(1); + @} + if (nerr == 0) + gencode(); + else + fprintf(stderr, "No code generated.\n"); +@asis{}<<<<<<< driver.c + exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +@asis{}======= + exit(!!nerr); +@asis{}>>>>>>> 1.6 +@} +@end example + +@noindent +@cindex Markers, conflict +@cindex Conflict markers +@cindex <<<<<<< +@cindex >>>>>>> +@cindex ======= + +Note how all non-overlapping modifications are incorporated in your working +copy, and that the overlapping section is clearly marked with +@samp{<<<<<<<}, @samp{=======} and @samp{>>>>>>>}. + +@cindex Resolving a conflict +@cindex Conflict resolution +You resolve the conflict by editing the file, removing the markers and +the erroneous line. Suppose you end up with this file: +@c -- Add xref to the pcl-cvs manual when it talks +@c -- about this. +@example +#include +#include + +int main(int argc, + char **argv) +@{ + init_scanner(); + parse(); + if (argc != 1) + @{ + fprintf(stderr, "tc: No args expected.\n"); + exit(1); + @} + if (nerr == 0) + gencode(); + else + fprintf(stderr, "No code generated.\n"); + exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +@} +@end example + +@noindent +You can now go ahead and commit this as revision 1.7. + +@example +$ cvs commit -m "Initialize scanner. Use symbolic exit values." driver.c +Checking in driver.c; +/usr/local/cvsroot/yoyodyne/tc/driver.c,v <-- driver.c +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. In previous +versions of @sc{cvs}, you also needed to +insure that the file contains no conflict markers. +Because +your file may legitimately contain conflict markers (that +is, occurrences of @samp{>>>>>>> } at the start of a +line that don't mark a conflict), the current +version of @sc{cvs} will print a warning and proceed to +check in the file. +@c The old behavior was really icky; the only way out +@c was to start hacking on +@c the @code{CVS/Entries} file or other such workarounds. +@c +@c If the timestamp thing isn't considered nice enough, +@c maybe 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 +package called emerge to help you resolve conflicts. +See the documentation for pcl-cvs. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Informing others +@section Informing others about commits +@cindex Informing others +@cindex Spreading information +@cindex Mail, automatic mail on commit + +It is often useful to inform others when you commit a +new revision of a file. The @samp{-i} option of the +@file{modules} file, or the @file{loginfo} file, can be +used to automate this process. @xref{modules}. +@xref{loginfo}. You can use these features of @sc{cvs} +to, for instance, instruct @sc{cvs} to mail a +message to all developers, or post a message to a local +newsgroup. +@c -- More text would be nice here. + +@node Concurrency +@section Several developers simultaneously attempting to run CVS + +@cindex Locks, cvs, introduction +@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: + +@example +[11:43:23] waiting for bach's lock in /usr/local/cvsroot/foo +@end example + +@cindex #cvs.rfl, removing +@cindex #cvs.wfl, removing +@cindex #cvs.lock, removing +@sc{cvs} will try again every 30 seconds, and either +continue with the operation or print the message again, +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 in the repository directory mentioned in +the message and remove files which they own whose names +start with @file{#cvs.rfl}, +@file{#cvs.wfl}, or @file{#cvs.lock}. + +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}---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 +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: + +@quotation +If someone commits some changes in one cvs command, +then an update by someone else will either get all the +changes, or none of them. +@end quotation + +@noindent +but @sc{cvs} does @emph{not} have this property. For +example, given the files + +@example +a/one.c +a/two.c +b/three.c +b/four.c +@end example + +@noindent +if someone runs + +@example +cvs ci a/two.c b/three.c +@end example + +@noindent +and someone else runs @code{cvs update} at the same +time, the person running @code{update} might get only +the change to @file{b/three.c} and not the change to +@file{a/two.c}. + +@node Watches +@section Mechanisms to track who is editing files +@cindex Watches + +For many groups, use of @sc{cvs} in its default mode is +perfectly satisfactory. Users may sometimes go to +check in a modification only to find that another +modification has intervened, but they deal with it and +proceed with their check in. Other groups prefer to be +able to know who is editing what files, so that if two +people try to edit the same file they can choose to +talk about who is doing what when rather than be +surprised at check in time. The features in this +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 +discard a working directory which is no longer in use, +but @sc{cvs} is not able to enforce this behavior. + +@c I'm a little dissatisfied with this presentation, +@c because "watch on"/"edit"/"editors" are one set of +@c functionality, and "watch add"/"watchers" is another +@c which is somewhat orthogonal even though they interact in +@c various ways. But I think it might be +@c confusing to describe them separately (e.g. "watch +@c add" with loginfo). I don't know. + +@menu +* Setting a watch:: Telling CVS to watch certain files +* Getting Notified:: Telling CVS to notify you +* Editing files:: How to edit a file which is being watched +* Watch information:: Information about who is watching and editing +* Watches Compatibility:: Watches interact poorly with CVS 1.6 or earlier +@end menu + +@node Setting a watch +@subsection Telling CVS to watch certain files + +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{-lR}] [@var{files}]@dots{} + +@cindex Read-only files, and watches +Specify that developers should run @code{cvs edit} +before editing @var{files}. @sc{cvs} will create working +copies of @var{files} read-only, to remind developers +to run the @code{cvs edit} command before working on +them. + +If @var{files} includes the name of a directory, @sc{cvs} +arranges to watch all files added to the corresponding +repository directory, and sets a default for files +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{-lR}] [@var{files}]@dots{} + +Do not create @var{files} read-only on checkout; thus, +developers will not be reminded to use @code{cvs edit} +and @code{cvs unedit}. +@ignore +@sc{cvs} will check out @var{files} +read-write as usual, unless other permissions override +due to the @code{PreservePermissions} option being +enabled in the @file{config} administrative file +(@pxref{Special Files}, @pxref{config}) +@end ignore + +The @var{files} and options are processed as for @code{cvs +watch on}. + +@end deffn + +@node Getting Notified +@subsection Telling CVS to notify you + +You can tell @sc{cvs} that you want to receive +notifications about various actions taken on a file. +You can do this without using @code{cvs watch on} for +the file, but generally you will want to use @code{cvs +watch on}, to remind developers to use the @code{cvs edit} +command. + +@cindex watch add (subcommand) +@deffn Command {cvs watch add} [@code{-lR}] [@code{-a} @var{action}]@dots{} [@var{files}]@dots{} + +Add the current user to the list of people to receive notification of +work done on @var{files}. + +The @code{-a} option specifies what kinds of events @sc{cvs} should notify +the user about. @var{action} is one of the following: + +@table @code + +@item edit +Another user has applied the @code{cvs edit} command (described +below) to a watched file. + +@item commit +Another user has committed changes to one of the named @var{files}. + +@item unedit +Another user has abandoned editing a file (other than by committing changes). +They can do this in several ways, by: + +@itemize @bullet + +@item +applying the @code{cvs unedit} command (described below) to the file + +@item +applying the @code{cvs release} command (@pxref{release}) to the file's parent directory +(or recursively to a directory more than one level up) + +@item +deleting the file and allowing @code{cvs update} to recreate it + +@end itemize + +@item all +All of the above. + +@item none +None of the above. (This is useful with @code{cvs edit}, +described below.) + +@end table + +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 options are processed as for +@code{cvs watch on}. + +@end deffn + + +@cindex watch remove (subcommand) +@deffn Command {cvs watch remove} [@code{-lR}] [@code{-a} @var{action}]@dots{} [@var{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 +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. 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 occurrence 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 + +@noindent +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 +way, users receive notifications on the server machine. +One could of course write a @file{notify} script which +directed notifications elsewhere, but to make this +easy, @sc{cvs} allows you to associate a notification +address for each user. To do so create a file +@file{users} in @file{CVSROOT} with a line for each +user in the format @var{user}:@var{value}. Then +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. 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} [@code{-lR}] [@code{-a} @var{action}]@dots{} [@var{files}]@dots{} + +Prepare to edit the working files @var{files}. @sc{cvs} makes the +@var{files} read-write, and notifies users who have requested +@code{edit} notification for any of @var{files}. + +The @code{cvs edit} command accepts the same options as the +@code{cvs watch add} command, and establishes a temporary watch for the +user on @var{files}; @sc{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 the options are processed as for the @code{cvs +watch} commands. + +@ignore +@strong{Caution: If the @code{PreservePermissions} +option is enabled in the repository (@pxref{config}), +@sc{cvs} will not change the permissions on any of the +@var{files}. The reason for this change is to ensure +that using @samp{cvs edit} does not interfere with the +ability to store file permissions in the @sc{cvs} +repository.} +@end ignore + +@end deffn + +Normally when you are done with a set of changes, you +use the @code{cvs commit} command, which checks in your +changes and returns the watched files to their usual +read-only state. But if you instead decide to abandon +your changes, or not to make any changes, you can use +the @code{cvs unedit} command. + +@cindex unedit (subcommand) +@cindex Abandoning work +@cindex Reverting to repository version +@deffn Command {cvs unedit} [@code{-lR}] [@var{files}]@dots{} + +Abandon work on the working files @var{files}, and revert them to the +repository versions on which they are based. @sc{cvs} makes those +@var{files} read-only for which users have requested notification using +@code{cvs watch on}. @sc{cvs} notifies users who have requested @code{unedit} +notification for any of @var{files}. + +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 with the command @code{cvs update -C file} +(@pxref{update}). +The meaning is +not precisely the same; the latter 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 +@code{cvs edit} and @code{cvs unedit} commands even if +@sc{cvs} is unable to successfully communicate with the +server; the notifications will be sent upon the next +successful @sc{cvs} command. + +@node Watch information +@subsection Information about who is watching and editing + +@cindex watchers (subcommand) +@deffn Command {cvs watchers} [@code{-lR}] [@var{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 options are processed as for the +@code{cvs watch} commands. + +@end deffn + + +@cindex editors (subcommand) +@deffn Command {cvs editors} [@code{-lR}] [@var{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 options are processed as for the +@code{cvs watch} commands. + +@end deffn + +@node Watches Compatibility +@subsection Using watches with old versions of CVS + +@cindex CVS 1.6, and watches +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 the +following (all on one line): + +@example +cvs update: cannot open CVS/Entries for reading: +No such file or directory +@end example + +@noindent +and your operation will likely be aborted. To use the +watch features, you must upgrade all copies of @sc{cvs} +which use that repository in local or server mode. If +you cannot upgrade, use the @code{watch off} and +@code{watch remove} commands to remove all watches, 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 Revision management +@chapter Revision management +@cindex Revision management + +@c -- This chapter could be expanded a lot. +@c -- Experiences are very welcome! + +If you have read this far, you probably have a pretty +good grasp on what @sc{cvs} can do for you. This +chapter talks a little about things that you still have +to decide. + +If you are doing development on your own using @sc{cvs} +you could probably skip this chapter. The questions +this chapter takes up become more important when more +than one person is working in a repository. + +@menu +* When to commit:: Some discussion on the subject +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node When to commit +@section When to commit? +@cindex When to commit +@cindex Committing, when to +@cindex Policy + +Your group should decide which policy to use regarding +commits. Several policies are possible, and as your +experience with @sc{cvs} grows you will probably find +out what works for you. + +If you commit files too quickly you might commit files +that do not even compile. If your partner updates his +working sources to include your buggy file, he will be +unable to compile the code. On the other hand, other +persons will not be able to benefit from the +improvements you make to the code if you commit very +seldom, and conflicts will probably be more common. + +It is common to only commit files after making sure +that they can be compiled. Some sites require that the +files pass a test suite. Policies like this can be +enforced using the commitinfo file +(@pxref{commitinfo}), but you should think twice before +you enforce such a convention. By making the +development environment too controlled it might become +too regimented and thus counter-productive to the real +goal, which is to get software written. + +@c --------------------------------------------------------------------- +@node Keyword substitution +@chapter Keyword substitution +@cindex Keyword substitution +@cindex Keyword expansion +@cindex Identifying files + +@comment Be careful when editing this chapter. +@comment Remember that this file is kept under +@comment version control, so we must not accidentally +@comment include a valid keyword in the running text. + +As long as you edit source files inside a working +directory you can always find out the state of +your files via @samp{cvs status} and @samp{cvs log}. +But as soon as you export the files from your +development environment it becomes harder to identify +which revisions they are. + +@sc{cvs} can use a mechanism known as @dfn{keyword +substitution} (or @dfn{keyword expansion}) to help +identifying the files. Embedded strings of the form +@code{$@var{keyword}$} and +@code{$@var{keyword}:@dots{}$} in a file are replaced +with strings of the form +@code{$@var{keyword}:@var{value}$} whenever you obtain +a new revision of the file. + +@menu +* Keyword list:: Keywords +* Using keywords:: Using keywords +* Avoiding substitution:: Avoiding substitution +* Substitution modes:: Substitution modes +* Configuring keyword expansion:: Configuring keyword expansion +* Log keyword:: Problems with the $@splitrcskeyword{Log}$ keyword. +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Keyword list +@section Keyword List +@cindex Keyword List + +@c FIXME: need some kind of example here I think, +@c perhaps in a +@c "Keyword intro" node. The intro in the "Keyword +@c substitution" node itself seems OK, but to launch +@c into a list of the keywords somehow seems too abrupt. + +This is a list of the keywords: + +@table @code +@cindex Author keyword +@item $@splitrcskeyword{Author}$ +The login name of the user who checked in the revision. + +@cindex CVSHeader keyword +@item $@splitrcskeyword{CVSHeader} +A standard header (similar to $@splitrcskeyword{Header}$, but with +the CVS root stripped off). It contains the relative +pathname of the @sc{rcs} file to the CVS root, the +revision number, the date (UTC), the author, the state, +and the locker (if locked). Files will normally never +be locked when you use @sc{cvs}. + +Note that this keyword has only been recently +introduced to @sc{cvs} and may cause problems with +existing installations if $@splitrcskeyword{CVSHeader}$ is already +in the files for a different purpose. This keyword may +be excluded using the @code{KeywordExpansion=eCVSHeader} +in the @file{CVSROOT/config} file. +See @ref{Configuring keyword expansion} for more details. + +@cindex Date keyword +@item $@splitrcskeyword{Date}$ +The date and time (UTC) the revision was checked in. + +@cindex Header keyword +@item $@splitrcskeyword{Header}$ +A standard header containing the full pathname of the +@sc{rcs} file, the revision number, the date (UTC), the +author, the state, and the locker (if locked). Files +will normally never be locked when you use @sc{cvs}. + +@cindex Id keyword +@item $@splitrcskeyword{Id}$ +Same as @code{$@splitrcskeyword{Header}$}, except that the @sc{rcs} +filename is without a path. + +@cindex Name keyword +@item $@splitrcskeyword{Name}$ +Tag name used to check out this file. The keyword is +expanded only if one checks out with an explicit tag +name. For example, when running the command @code{cvs +co -r first}, the keyword expands to @samp{Name: first}. + +@cindex Locker keyword +@item $@splitrcskeyword{Locker}$ +The login name of the user who locked the revision +(empty if not locked, which is the normal case unless +@code{cvs admin -l} is in use). + +@cindex Log keyword +@item $@splitrcskeyword{Log}$ +The log message supplied during commit, preceded by a +header containing the @sc{rcs} filename, the revision +number, the author, and the date (UTC). Existing log +messages are @emph{not} replaced. Instead, the new log +message is inserted after @code{$@splitrcskeyword{Log}:@dots{}$}. +Each new line is prefixed with the same string which +precedes the @code{$Log} keyword. For example, if the +file contains: + +@example + /* Here is what people have been up to: + * + * $@splitrcskeyword{Log}: frob.c,v $ + * Revision 1.1 1997/01/03 14:23:51 joe + * Add the superfrobnicate option + * + */ +@end example + +@noindent +then additional lines which are added when expanding +the @code{$Log} keyword will be preceded by @samp{ * }. +Unlike previous versions of @sc{cvs} and @sc{rcs}, the +@dfn{comment leader} from the @sc{rcs} file is not used. +The @code{$Log} keyword is useful for +accumulating a complete change log in a source file, +but for several reasons it can be problematic. +@xref{Log keyword}. + +@cindex RCSfile keyword +@item $@splitrcskeyword{RCSfile}$ +The name of the RCS file without a path. + +@cindex Revision keyword +@item $@splitrcskeyword{Revision}$ +The revision number assigned to the revision. + +@cindex Source keyword +@item $@splitrcskeyword{Source}$ +The full pathname of the RCS file. + +@cindex State keyword +@item $@splitrcskeyword{State}$ +The state assigned to the revision. States can be +assigned with @code{cvs admin -s}---see @ref{admin options}. + +@cindex Local keyword +@item Local keyword +The @code{LocalKeyword} option in the @file{CVSROOT/config} file +may be used to specify a local keyword which is to be +used as an alias for one of the other keywords. For +example, if the @file{CVSROOT/config} file contains +a line with @code{LocalKeyword=MYBSD=CVSHeader}, then a +file with the local keyword $@splitrcskeyword{MYBSD}$ will be +expanded as if it were a $@splitrcskeyword{CVSHeader}$ keyword. If +the src/frob.c file contained this keyword, it might +look something like this: + +@example + /* + * $@splitrcskeyword{MYBSD}: src/frob.c,v 1.1 2003/05/04 09:27:45 john Exp $ + */ +@end example + +Many repositories make use of a such a ``local +keyword'' feature. An old patch to @sc{cvs} provided +the @code{LocalKeyword} feature using a @code{tag=} +option and called this the ``custom tag'' or ``local +tag'' feature. It was used in conjunction with the +what they called the @code{tagexpand=} option. In +@sc{cvs} this other option is known as the +@code{KeywordExpand} option. +See @ref{Configuring keyword expansion} for more +details. + +Examples from popular projects include: +$@splitrcskeyword{FreeBSD}$, $@splitrcskeyword{NetBSD}$, +$@splitrcskeyword{OpenBSD}$, $@splitrcskeyword{XFree86}$, +$@splitrcskeyword{Xorg}$. + +The advantage of this is that you can include your +local version information in a file using this local +keyword without disrupting the upstream version +information (which may be a different local keyword or +a standard keyword). Allowing bug reports and the like +to more properly identify the source of the original +bug to the third-party and reducing the number of +conflicts that arise during an import of a new version. + +All keyword expansion except the local keyword may be +disabled using the @code{KeywordExpansion} option in +the @file{CVSROOT/config} file---see +@ref{Configuring keyword expansion} for more details. + +@end table + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Using keywords +@section Using keywords + +To include a keyword string you simply include the +relevant text string, such as @code{$@splitrcskeyword{Id}$}, inside the +file, and commit the file. @sc{cvs} will automatically (Or, +more accurately, as part of the update run that +automatically happens after a commit.) +expand the string as part of the commit operation. + +It is common to embed the @code{$@splitrcskeyword{Id}$} string in +the source files so that it gets passed through to +generated files. For example, if you are managing +computer program source code, you might include a +variable which is initialized to contain that string. +Or some C compilers may provide a @code{#pragma ident} +directive. Or a document management system might +provide a way to pass a string through to generated +files. + +@c Would be nice to give an example, but doing this in +@c portable C is not possible and the problem with +@c picking any one language (VMS HELP files, Ada, +@c troff, whatever) is that people use CVS for all +@c kinds of files. + +@cindex Ident (shell command) +The @code{ident} command (which is part of the @sc{rcs} +package) can be used to extract keywords and their +values from a file. This can be handy for text files, +but it is even more useful for extracting keywords from +binary files. + +@example +$ ident samp.c +samp.c: + $@splitrcskeyword{Id}: samp.c,v 1.5 1993/10/19 14:57:32 ceder Exp $ +$ gcc samp.c +$ ident a.out +a.out: + $@splitrcskeyword{Id}: samp.c,v 1.5 1993/10/19 14:57:32 ceder Exp $ +@end example + +@cindex What (shell command) +S@sc{ccs} is another popular revision control system. +It has a command, @code{what}, which is very similar to +@code{ident} and used for the same purpose. Many sites +without @sc{rcs} have @sc{sccs}. Since @code{what} +looks for the character sequence @code{@@(#)} it is +easy to include keywords that are detected by either +command. Simply prefix the keyword with the +magic @sc{sccs} phrase, like this: + +@example +static char *id="@@(#) $@splitrcskeyword{Id}: ab.c,v 1.5 1993/10/19 14:57:32 ceder Exp $"; +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Avoiding substitution +@section Avoiding substitution + +Keyword substitution has its disadvantages. Sometimes +you might want the literal text string +@samp{$@splitrcskeyword{Author}$} to appear inside a file without +@sc{cvs} interpreting it as a keyword and expanding it +into something like @samp{$@splitrcskeyword{Author}: ceder $}. + +There is unfortunately no way to selectively turn off +keyword substitution. You can use @samp{-ko} +(@pxref{Substitution modes}) to turn off keyword +substitution entirely. + +In many cases you can avoid using keywords in +the source, even though they appear in the final +product. For example, the source for this manual +contains @samp{$@@asis@{@}Author$} whenever the text +@samp{$@splitrcskeyword{Author}$} should appear. In @code{nroff} +and @code{troff} you can embed the null-character +@code{\&} inside the keyword for a similar effect. + +It is also possible to specify an explicit list of +keywords to include or exclude using the +@code{KeywordExpand} option in the +@file{CVSROOT/config} file--see @ref{Configuring keyword expansion} +for more details. This feature is intended primarily +for use with the @code{LocalKeyword} option--see +@ref{Keyword list}. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Substitution modes +@section Substitution modes +@cindex Keyword substitution, changing modes +@cindex -k (keyword substitution) +@cindex Kflag + +@c FIXME: This could be made more coherent, by expanding it +@c with more examples or something. +Each file has a stored default substitution mode, and +each working directory copy of a file also has a +substitution mode. The former is set by the @samp{-k} +option to @code{cvs add} and @code{cvs admin}; the +latter is set by the @samp{-k} or @samp{-A} options to @code{cvs +checkout} or @code{cvs update}. @code{cvs diff} also +has a @samp{-k} option. For some examples, +see @ref{Binary files}, and @ref{Merging and keywords}. +@c The fact that -A is overloaded to mean both reset +@c sticky options and reset sticky tags/dates is +@c somewhat questionable. Perhaps there should be +@c separate options to reset sticky options (e.g. -k +@c A") and tags/dates (someone suggested -r HEAD could +@c do this instead of setting a sticky tag of "HEAD" +@c as in the status quo but I haven't thought much +@c about that idea. Of course -r .reset or something +@c could be coined if this needs to be a new option). +@c On the other hand, having -A mean "get things back +@c into the state after a fresh checkout" has a certain +@c appeal, and maybe there is no sufficient reason for +@c creeping featurism in this area. + +The modes available are: + +@table @samp +@item -kkv +Generate keyword strings using the default form, e.g. +@code{$@splitrcskeyword{Revision}: 5.7 $} for the @code{Revision} +keyword. + +@item -kkvl +Like @samp{-kkv}, except that a locker's name is always +inserted if the given revision is currently locked. +The locker's name is only relevant if @code{cvs admin +-l} is in use. + +@item -kk +Generate only keyword names in keyword strings; omit +their values. For example, for the @code{Revision} +keyword, generate the string @code{$@splitrcskeyword{Revision}$} +instead of @code{$@splitrcskeyword{Revision}: 5.7 $}. This option +is useful to ignore differences due to keyword +substitution when comparing different revisions of a +file (@pxref{Merging and keywords}). + +@item -ko +Generate the old keyword string, present in the working +file just before it was checked in. For example, for +the @code{Revision} keyword, generate the string +@code{$@splitrcskeyword{Revision}: 1.1 $} instead of +@code{$@splitrcskeyword{Revision}: 5.7 $} if that is how the +string appeared when the file was checked in. + +@item -kb +Like @samp{-ko}, but also inhibit conversion of line +endings between the canonical form in which they are +stored in the repository (linefeed only), and the form +appropriate to the operating system in use on the +client. For systems, like unix, which use linefeed +only to terminate lines, this is very similar to +@samp{-ko}. For more information on binary files, see +@ref{Binary files}. In @sc{cvs} version 1.12.2 and later +@samp{-kb}, as set by @code{cvs add}, @code{cvs admin}, or +@code{cvs import} may not be overridden by a @samp{-k} option +specified on the command line. + +@item -kv +Generate only keyword values for keyword strings. For +example, for the @code{Revision} keyword, generate the string +@code{5.7} instead of @code{$@splitrcskeyword{Revision}: 5.7 $}. +This can help generate files in programming languages +where it is hard to strip keyword delimiters like +@code{$@splitrcskeyword{Revision}: $} from a string. However, +further keyword substitution cannot be performed once +the keyword names are removed, so this option should be +used with care. + +One often would like to use @samp{-kv} with @code{cvs +export}---@pxref{export}. But be aware that doesn't +handle an export containing binary files correctly. + +@end table + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Configuring keyword expansion +@section Configuring Keyword Expansion +@cindex Configuring keyword expansion + +In a repository that includes third-party software on +vendor branches, it is sometimes helpful to configure +CVS to use a local keyword instead of the standard +$@splitrcskeyword{Id}$ or $@splitrcskeyword{Header}$ keywords. Examples from +real projects include $@splitrcskeyword{Xorg}$, $@splitrcskeyword{XFree86}$, +$@splitrcskeyword{FreeBSD}$, $@splitrcskeyword{NetBSD}$, +$@splitrcskeyword{OpenBSD}$, and even $@splitrcskeyword{dotat}$. +The advantage of this is that +you can include your local version information in a +file using this local keyword (sometimes called a +``custom tag'' or a ``local tag'') without disrupting +the upstream version information (which may be a +different local keyword or a standard keyword). In +these cases, it is typically desirable to disable the +expansion of all keywords except the configured local +keyword. + +The @code{KeywordExpansion} option in the +@file{CVSROOT/config} file is intended to allow for the +either the explicit exclusion of a keyword or list of +keywords, or for the explicit inclusion of a keyword or +a list of keywords. This list may include the +@code{LocalKeyword} that has been configured. + +The @code{KeywordExpansion} option is followed by +@code{=} and the next character may either be @code{i} +to start an inclusion list or @code{e} to start an +exclusion list. If the following lines were added to +the @file{CVSROOT/config} file: + +@example + # Add a "MyBSD" keyword and restrict keyword + # expansion + LocalKeyword=MyBSD=CVSHeader + KeywordExpand=iMyBSD +@end example + +then only the $@splitrcskeyword{MyBSD}$ keyword would be expanded. +A list may be used. The this example: + +@example + # Add a "MyBSD" keyword and restrict keyword + # expansion to the MyBSD, Name and Date keywords. + LocalKeyword=MyBSD=CVSHeader + KeywordExpand=iMyBSD,Name,Date +@end example + +would allow $@splitrcskeyword{MyBSD}$, $@splitrcskeyword{Name}$, and +$@splitrcskeyword{Date}$ to be expanded. + +It is also possible to configure an exclusion list +using the following: + +@example + # Do not expand the non-RCS keyword CVSHeader + KeywordExpand=eCVSHeader +@end example + +This allows @sc{cvs} to ignore the recently introduced +$@splitrcskeyword{CVSHeader}$ keyword and retain all of the +others. The exclusion entry could also contain the +standard RCS keyword list, but this could be confusing +to users that expect RCS keywords to be expanded, so +care should be taken to properly set user expectations +for a repository that is configured in that manner. + +If there is a desire to not have any RCS keywords +expanded and not use the @code{-ko} flags everywhere, +an administrator may disable all keyword expansion +using the @file{CVSROOT/config} line: + +@example + # Do not expand any RCS keywords + KeywordExpand=i +@end example + +this could be confusing to users that expect RCS +keywords like $@splitrcskeyword{Id}$ to be expanded properly, +so care should be taken to properly set user +expectations for a repository so configured. + +It should be noted that a patch to provide both the +@code{KeywordExpand} and @code{LocalKeyword} features +has been around a long time. However, that patch +implemented these features using @code{tag=} and +@code{tagexpand=} keywords and those keywords are NOT +recognized. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Log keyword +@section Problems with the $@splitrcskeyword{Log}$ keyword. + +The @code{$@splitrcskeyword{Log}$} keyword is somewhat +controversial. As long as you are working on your +development system the information is easily accessible +even if you do not use the @code{$@splitrcskeyword{Log}$} +keyword---just do a @code{cvs log}. Once you export +the file the history information might be useless +anyhow. + +A more serious concern is that @sc{cvs} is not good at +handling @code{$@splitrcskeyword{Log}$} entries when a branch is +merged onto the main trunk. Conflicts often result +from the merging operation. +@c Might want to check whether the CVS implementation +@c of RCS_merge has this problem the same way rcsmerge +@c does. I would assume so.... + +People also tend to "fix" the log entries in the file +(correcting spelling mistakes and maybe even factual +errors). If that is done the information from +@code{cvs log} will not be consistent with the +information inside the file. This may or may not be a +problem in real life. + +It has been suggested that the @code{$@splitrcskeyword{Log}$} +keyword should be inserted @emph{last} in the file, and +not in the files header, if it is to be used at all. +That way the long list of change messages will not +interfere with everyday source file browsing. + +@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. +@c FIXME: This doesn't really adequately introduce the +@c concepts of "vendor" and "you". They don't *have* +@c to be separate organizations or separate people. +@c We want a description which is somewhat more based on +@c the technical issues of which sources go where, but +@c also with enough examples of how this relates to +@c relationships like customer-supplier, developer-QA, +@c maintainer-contributor, or whatever, to make it +@c seem concrete. +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 +this task. + +@cindex Vendor +@cindex Vendor branch +@cindex Branch, vendor- +In the terminology used in @sc{cvs}, the supplier of the +program is called a @dfn{vendor}. The unmodified +distribution from the vendor is checked in on its own +branch, the @dfn{vendor branch}. @sc{cvs} reserves branch +1.1.1 for this use. + +When you modify the source and commit it, your revision +will end up on the main trunk. When a new release is +made by the vendor, you commit it on the vendor branch +and copy the modifications onto the main trunk. + +Use the @code{import} command to create and update +the vendor branch. When you import a new file, +the vendor branch is made the `head' revision, so +anyone that checks out a copy of the file gets that +revision. When a local modification is committed it is +placed on the main trunk, and made the `head' +revision. + +@menu +* First import:: Importing for the first time +* Update imports:: Updating with the import command +* Reverting local changes:: Reverting to the latest vendor release +* Binary files in imports:: Binary files require special handling +* Keywords in imports:: Keyword substitution might be undesirable +* Multiple vendor branches:: What if you get sources from several places? +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node First import +@section Importing 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---see @ref{Multiple vendor branches}.). 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 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 +$ cd wdiff-0.04 +$ cvs import -m "Import of FSF v. 0.04" fsf/wdiff FSF_DIST WDIFF_0_04 +@end example + +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 +@section Updating with the import command + +When a new release of the source arrives, you import it into the +repository with the same @code{import} command that you used to set up +the repository in the first place. The only difference is that you +specify a different release tag this time: + +@example +$ tar xfz wdiff-0.05.tar.gz +$ cd wdiff-0.05 +$ cvs import -m "Import of FSF v. 0.05" fsf/wdiff FSF_DIST WDIFF_0_05 +@end example + +For files that have not been modified locally, the newly created +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 + +@noindent +The above command will check out the latest revision of +@samp{wdiff}, merging the changes made on the vendor branch @samp{FSF_DIST} +since yesterday into the working copy. If any conflicts arise during +the merge they should be resolved in the normal way (@pxref{Conflicts +example}). Then, the modified files may be committed. + +However, it is much better to use the two release tags rather than using +a date on the branch as suggested above: + +@example +$ cvs checkout -jWDIFF_0_04 -jWDIFF_0_05 wdiff +@end example + +@noindent +The reason this is better is that +using a date, as suggested above, assumes that you do +not import more than one release of a product per day. +More importantly, using the release tags allows @sc{cvs} to detect files +that were removed between the two vendor releases and mark them for +removal. Since @code{import} has no way to detect removed files, you +should do a merge like this even if @code{import} doesn't tell you to. + +@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 -bFSF_DIST . +@end example + +@noindent +You must specify the @samp{-bFSF_DIST} 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}. + +@node Keywords in imports +@section How to handle keyword substitution with cvs import + +The sources which you are importing may contain +keywords (@pxref{Keyword substitution}). For example, +the vendor may use @sc{cvs} or some other system +which uses similar keyword expansion syntax. If you +just import the files in the default fashion, then +the keyword expansions supplied by the vendor will +be replaced by keyword expansions supplied by your +own copy of @sc{cvs}. It may be more convenient to +maintain the expansions supplied by the vendor, so +that this information can supply information about +the sources that you imported from the vendor. + +To maintain the keyword expansions supplied by the +vendor, supply the @samp{-ko} option to @code{cvs +import} the first time you import the file. +This will turn off keyword expansion +for that file entirely, so if you want to be more +selective you'll have to think about what you want +and use the @samp{-k} option to @code{cvs update} or +@code{cvs admin} as appropriate. +@c Supplying -ko to import if the file already existed +@c has no effect. Not clear to me whether it should +@c or not. + +@node Multiple vendor branches +@section Multiple vendor branches + +All the examples so far assume that there is only one +vendor from which you are getting sources. In some +situations you might get sources from a variety of +places. For example, suppose that you are dealing with +a project where many different people and teams are +modifying the software. There are a variety of ways to +handle this, but in some cases you have a bunch of +source trees lying around and what you want to do more +than anything else is just to all put them in @sc{cvs} so +that you at least have them in one place. + +For handling situations in which there may be more than +one vendor, you may specify the @samp{-b} option to +@code{cvs import}. It takes as an argument the vendor +branch to import to. The default is @samp{-b 1.1.1}. + +For example, suppose that there are two teams, the red +team and the blue team, that are sending you sources. +You want to import the red team's efforts to branch +1.1.1 and use the vendor tag RED. You want to import +the blue team's efforts to branch 1.1.3 and use the +vendor tag BLUE. So the commands you might use are: + +@example +$ cvs import dir RED RED_1-0 +$ cvs import -b 1.1.3 dir BLUE BLUE_1-5 +@end example + +Note that if your vendor tag does not match your +@samp{-b} option, @sc{cvs} will not detect this case! For +example, + +@example +$ cvs import -b 1.1.3 dir RED RED_1-0 +@end example + +@noindent +Be careful; this kind of mismatch is sure to sow +confusion or worse. I can't think of a useful purpose +for the ability to specify a mismatch here, but if you +discover such a use, don't. @sc{cvs} is likely to make this +an error in some future release. + +@c Probably should say more about the semantics of +@c multiple branches. What about the default branch? +@c What about joining (perhaps not as useful with +@c multiple branches, or perhaps it is. Either way +@c should be mentioned). + +@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 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 no one 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.grin.net/~pzi/mk-3.18.4.docs/mk_toc.html +@c He has changed providers in the past; a search engine search +@c for "Peter Ziobrzynski" probably won't get too many +@c spurious hits :-). A more stable URL might be +@c ftp://ftp.uu.net/pub/cmvc/mk). But I'm not sure +@c there is any point in mentioning them here unless they +@c can work with CVS. + +@c --------------------------------------------------------------------- +@node Special Files +@chapter Special Files + +@cindex Special files +@cindex Device nodes +@cindex Ownership, saving in CVS +@cindex Permissions, saving in CVS +@cindex Hard links +@cindex Symbolic links + +In normal circumstances, @sc{cvs} works only with regular +files. Every file in a project is assumed to be +persistent; it must be possible to open, read and close +them; and so on. @sc{cvs} also ignores file permissions and +ownerships, leaving such issues to be resolved by the +developer at installation time. In other words, it is +not possible to "check in" a device into a repository; +if the device file cannot be opened, @sc{cvs} will refuse to +handle it. Files also lose their ownerships and +permissions during repository transactions. + +@ignore +If the configuration variable @code{PreservePermissions} +(@pxref{config}) is set in the repository, @sc{cvs} will +save the following file characteristics in the +repository: + +@itemize @bullet +@item user and group ownership +@item permissions +@item major and minor device numbers +@item symbolic links +@item hard link structure +@end itemize + +Using the @code{PreservePermissions} option affects the +behavior of @sc{cvs} in several ways. First, some of the +new operations supported by @sc{cvs} are not accessible to +all users. In particular, file ownership and special +file characteristics may only be changed by the +superuser. When the @code{PreservePermissions} +configuration variable is set, therefore, users will +have to be `root' in order to perform @sc{cvs} operations. + +When @code{PreservePermissions} is in use, some @sc{cvs} +operations (such as @samp{cvs status}) will not +recognize a file's hard link structure, and so will +emit spurious warnings about mismatching hard links. +The reason is that @sc{cvs}'s internal structure does not +make it easy for these operations to collect all the +necessary data about hard links, so they check for file +conflicts with inaccurate data. + +A more subtle difference is that @sc{cvs} considers a file +to have changed only if its contents have changed +(specifically, if the modification time of the working +file does not match that of the repository's file). +Therefore, if only the permissions, ownership or hard +linkage have changed, or if a device's major or minor +numbers have changed, @sc{cvs} will not notice. In order to +commit such a change to the repository, you must force +the commit with @samp{cvs commit -f}. This also means +that if a file's permissions have changed and the +repository file is newer than the working copy, +performing @samp{cvs update} will silently change the +permissions on the working copy. + +Changing hard links in a @sc{cvs} repository is particularly +delicate. Suppose that file @file{foo} is linked to +file @file{old}, but is later relinked to file +@file{new}. You can wind up in the unusual situation +where, although @file{foo}, @file{old} and @file{new} +have all had their underlying link patterns changed, +only @file{foo} and @file{new} have been modified, so +@file{old} is not considered a candidate for checking +in. It can be very easy to produce inconsistent +results this way. Therefore, we recommend that when it +is important to save hard links in a repository, the +prudent course of action is to @code{touch} any file +whose linkage or status has changed since the last +checkin. Indeed, it may be wise to @code{touch *} +before each commit in a directory with complex hard +link structures. + +It is worth noting that only regular files may +be merged, for reasons that hopefully are obvious. If +@samp{cvs update} or @samp{cvs checkout -j} attempts to +merge a symbolic link with a regular file, or two +device files for different kinds of devices, @sc{cvs} will +report a conflict and refuse to perform the merge. At +the same time, @samp{cvs diff} will not report any +differences between these files, since no meaningful +textual comparisons can be made on files which contain +no text. + +The @code{PreservePermissions} features do not work +with client/server @sc{cvs}. Another limitation is +that hard links must be to other files within the same +directory; hard links across directories are not +supported. +@end ignore + +@c --------------------------------------------------------------------- +@c ----- START MAN 1 ----- +@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. +@c +@c Note that many users do expect a manual which is +@c organized by command. At least some users do. +@c One good addition to the "organized by command" +@c section (if any) would be "see also" links. +@c The awk manual might be a good example; it has a +@c reference manual which is more verbose than Invoking +@c CVS but probably somewhat less verbose than CVS +@c Commands. + +@menu +* Structure:: Overall structure of CVS commands +* Exit status:: Indicating CVS's success or failure +* ~/.cvsrc:: Default options with the ~/.cvsrc file +* Global options:: Options you give to the left of cvs_command +* Common options:: Options you give to the right of cvs_command +* admin:: Administration +* annotate:: What revision modified each line of a file? +* checkout:: Checkout sources for editing +* commit:: Check files into the repository +* 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:: Show log messages for files +* ls & rls:: List files in the repository +* rdiff:: 'patch' format diffs between releases +* release:: Indicate that a directory is no longer in use +* update:: Bring work tree in sync with repository +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Structure +@appendixsec Overall structure of CVS commands +@cindex Structure +@cindex CVS command structure +@cindex Command structure +@cindex Format of CVS commands + +The overall format of all @sc{cvs} commands is: + +@example +cvs [ cvs_options ] cvs_command [ command_options ] [ command_args ] +@end example + +@table @code +@item cvs +The name of the @sc{cvs} program. + +@item cvs_options +Some options that affect all sub-commands of @sc{cvs}. These are +described below. + +@item cvs_command +One of several different sub-commands. Some of the commands have +aliases that can be used instead; those aliases are noted in the +reference manual for that command. There are only two situations +where you may omit @samp{cvs_command}: @samp{cvs -H} elicits a +list of available commands, and @samp{cvs -v} displays version +information on @sc{cvs} itself. + +@item command_options +Options that are specific for the command. + +@item command_args +Arguments to the commands. +@end table + +There is unfortunately some confusion between +@code{cvs_options} and @code{command_options}. +When given as a @code{cvs_option}, some options only +affect some of the commands. When given as a +@code{command_option} it may have a different meaning, and +be accepted by more commands. In other words, do not +take the above categorization too seriously. Look at +the documentation instead. + +@node Exit status +@appendixsec CVS's exit status +@cindex Exit status, of CVS + +@sc{cvs} can indicate to the calling environment whether it +succeeded or failed by setting its @dfn{exit status}. +The exact way of testing the exit status will vary from +one operating system to another. For example in a unix +shell script the @samp{$?} variable will be 0 if the +last command returned a successful exit status, or +greater than 0 if the exit status indicated failure. + +If @sc{cvs} is successful, it returns a successful status; +if there is an error, it prints an error message and +returns a failure status. The one exception to this is +the @code{cvs diff} command. It will return a +successful status if it found no differences, or a +failure status if there were differences or if there +was an error. Because this behavior provides no good +way to detect errors, in the future it is possible that +@code{cvs diff} will be changed to behave like the +other @sc{cvs} commands. +@c It might seem like checking whether cvs -q diff +@c produces empty or non-empty output can tell whether +@c there were differences or not. But it seems like +@c there are cases with output but no differences +@c (testsuite basica-8b). It is not clear to me how +@c useful it is for a script to be able to check +@c whether there were differences. +@c FIXCVS? In previous versions of CVS, cvs diff +@c returned 0 for no differences, 1 for differences, or +@c 2 for errors. Is this behavior worth trying to +@c bring back (but what does it mean for VMS?)? + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node ~/.cvsrc +@appendixsec Default options and the ~/.cvsrc file +@cindex .cvsrc file +@cindex Option defaults + +There are some @code{command_options} that are used so +often that you might have set up an alias or some other +means to make sure you always specify that option. One +example (the one that drove the implementation of the +@file{.cvsrc} support, actually) is that many people find the +default output of the @samp{diff} command to be very +hard to read, and that either context diffs or unidiffs +are much easier to understand. + +The @file{~/.cvsrc} file is a way that you can add +default options to @code{cvs_commands} within cvs, +instead of relying on aliases or other shell scripts. + +The format of the @file{~/.cvsrc} file is simple. The +file is searched for a line that begins with the same +name as the @code{cvs_command} being executed. If a +match is found, then the remainder of the line is split +up (at whitespace characters) into separate options and +added to the command arguments @emph{before} any +options from the command line. + +If a command has two names (e.g., @code{checkout} and +@code{co}), the official name, not necessarily the one +used on the command line, will be used to match against +the file. So if this is the contents of the user's +@file{~/.cvsrc} file: + +@example +log -N +diff -uN +rdiff -u +update -Pd +checkout -P +release -d +@end example + +@noindent +the command @samp{cvs checkout foo} would have the +@samp{-P} option added to the arguments, as well as +@samp{cvs co foo}. + +With the example file above, the output from @samp{cvs +diff foobar} will be in unidiff format. @samp{cvs diff +-c foobar} will provide context diffs, as usual. +Getting "old" format diffs would be slightly more +complicated, because @code{diff} doesn't have an option +to specify use of the "old" format, so you would need +@samp{cvs -f diff foobar}. + +In place of the command name you can use @code{cvs} to +specify global options (@pxref{Global options}). For +example the following line in @file{.cvsrc} + +@example +cvs -z6 +@end example + +@noindent +causes @sc{cvs} to use compression level 6. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Global options +@appendixsec Global options +@cindex Options, global +@cindex Global options +@cindex Left-hand options + +The available @samp{cvs_options} (that are given to the +left of @samp{cvs_command}) are: + +@table @code +@item --allow-root=@var{rootdir} +Specify legal @sc{cvsroot} directory. See +@ref{Password authentication server}. + +@cindex Authentication, stream +@cindex Stream authentication +@item -a +Authenticate 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 GSSAPI connection (@pxref{GSSAPI authenticated}). +Authentication prevents certain sorts of attacks +involving hijacking the active @sc{tcp} connection. +Enabling authentication does not enable encryption. + +@cindex RCSBIN, overriding +@cindex Overriding RCSBIN +@item -b @var{bindir} +In @sc{cvs} 1.9.18 and older, this specified that +@sc{rcs} programs are in the @var{bindir} directory. +Current versions of @sc{cvs} do not run @sc{rcs} +programs; for compatibility this option is accepted, +but it does nothing. + +@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. +(When running client/server, @samp{-T} affects only the local process; +specifying @samp{-T} for the client has no effect on the server and +vice versa.) + +@cindex CVSROOT, overriding +@cindex Overriding CVSROOT +@item -d @var{cvs_root_directory} +Use @var{cvs_root_directory} as the root directory +pathname of the repository. Overrides the setting of +the @code{$CVSROOT} environment variable. @xref{Repository}. + +@cindex EDITOR, overriding +@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. For more information, see +@ref{Committing your changes}. + +@item -f +Do not read the @file{~/.cvsrc} file. This +option is most often used because of the +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{log}, +you may need to use @samp{-f} to show the tag names. + +@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 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... + +@cindex Read-only repository mode +@item -R +Turns on read-only repository mode. This allows one to check out from a +read-only repository, such as within an anoncvs server, or from a @sc{cd-rom} +repository. + +Same effect as if the @code{CVSREADONLYFS} environment +variable is set. Using @samp{-R} can also considerably +speed up checkouts over NFS. + +@cindex Read-only mode +@item -n +Do not change any files. Attempt to execute the +@samp{cvs_command}, but only to issue reports; do not remove, +update, or merge any existing files, or create any new files. + +Note that @sc{cvs} will not necessarily produce exactly +the same output as without @samp{-n}. In some cases +the output will be the same, but in other cases +@sc{cvs} will skip some of the processing that would +have been required to produce the exact same output. + +@item -Q +Cause the command to be really quiet; the command will only +generate output for serious problems. + +@item -q +Cause the command to be somewhat quiet; informational messages, +such as reports of recursion through subdirectories, are +suppressed. + +@cindex Read-only files, and -r +@item -r +Make new working files read-only. Same effect +as if the @code{$CVSREAD} environment variable is set +(@pxref{Environment variables}). The default is to +make working files writable, unless watches are on +(@pxref{Watches}). + +@item -s @var{variable}=@var{value} +Set a user variable (@pxref{Variables}). + +@cindex Trace +@item -t +Trace program execution; display messages showing the steps of +@sc{cvs} activity. Particularly useful with @samp{-n} to explore the +potential impact of an unfamiliar command. + +@item -v +@item --version +Display version and copyright information for @sc{cvs}. + +@cindex CVSREAD, overriding +@cindex Overriding CVSREAD +@item -w +Make new working files read-write. Overrides the +setting of the @code{$CVSREAD} environment variable. +Files are created read-write by default, unless @code{$CVSREAD} is +set or @samp{-r} is given. +@c Note that -w only overrides -r and CVSREAD; it has +@c no effect on files which are readonly because of +@c "cvs watch on". My guess is that is the way it +@c should be (or should "cvs -w get" on a watched file +@c be the same as a get and a cvs edit?), but I'm not +@c completely sure whether to document it this way. + +@item -x +@cindex Encryption +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 +GSSAPI connection (@pxref{GSSAPI authenticated}) or a +Kerberos connection (@pxref{Kerberos authenticated}). +Enabling encryption implies that message traffic is +also 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} +@cindex Compression +@cindex Gzip +Set the compression level. +Valid levels are 1 (high speed, low compression) to +9 (low speed, high compression), or 0 to disable +compression (the default). +Only has an effect on the @sc{cvs} client. + +@end table + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Common options +@appendixsec Common command options +@cindex Common options +@cindex Right-hand options + +This section describes the @samp{command_options} that +are available across several @sc{cvs} commands. These +options are always given to the right of +@samp{cvs_command}. Not all +commands support all of these options; each option is +only supported for commands where it makes sense. +However, when a command has one of these options you +can almost always count on the same behavior of the +option as in other commands. (Other command options, +which are listed with the individual commands, may have +different behavior from one @sc{cvs} command to the other). + +@strong{Note: the @samp{history} command is an exception; it supports +many options that conflict even with these standard options.} + +@table @code +@cindex Dates +@cindex Time +@cindex Specifying dates +@item -D @var{date_spec} +Use the most recent revision no later than @var{date_spec}. +@var{date_spec} is a single argument, a date description +specifying a date in the past. + +The specification is @dfn{sticky} when you use it to make a +private copy of a source file; that is, when you get a working +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}). + +@samp{-D} is available with the @code{annotate}, @code{checkout}, +@code{diff}, @code{export}, @code{history}, @code{ls}, +@code{rdiff}, @code{rls}, @code{rtag}, @code{tag}, and @code{update} commands. +(The @code{history} command uses this option in a +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. + +There are a lot more ISO8601 date formats, and @sc{cvs} +accepts many of them, but you probably don't want to +hear the @emph{whole} long story :-). + +@c Citing a URL here is kind of problematic given how +@c much they change and people who have old versions of +@c this manual, but in case we want to reinstate an +@c ISO8601 URL, a few are: +@c http://www.saqqara.demon.co.uk/datefmt.htm +@c http://www.cl.cam.ac.uk/~mgk25/iso-time.html +@c Citing some other ISO8601 source is probably even +@c worse :-). + +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 We should document and testsuite "now" and +@c "yesterday". "now" is mentioned in the FAQ and +@c "yesterday" is mentioned in this document (and the +@c message from "cvs import" suggesting a merge +@c command). What else? Probably some/all of the "3 +@c weeks ago" family. +@c +@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 +argument separators. A command using the @samp{-D} +flag can look like this: + +@example +$ cvs diff -D "1 hour ago" cvs.texinfo +@end example + +@cindex Forcing a tag match +@item -f +When you specify a particular date or tag to @sc{cvs} commands, they +normally ignore files that do not contain the tag (or did not +exist prior to the date) that you specified. Use the @samp{-f} option +if you want files retrieved even when there is no match for the +tag or date. (The most recent revision of the file +will be used). + +Note that even with @samp{-f}, a tag that you specify +must exist (that is, in some file, not necessary in +every file). This is so that @sc{cvs} will continue to +give an error if you mistype a tag name. + +@need 800 +@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} and @code{remove} +commands also have a +@samp{-f} option, but it has a different behavior for +those commands. See @ref{commit options}, and +@ref{Removing files}.} + +@item -k @var{kflag} +Override the default processing of RCS keywords other than +@samp{-kb}. @xref{Keyword substitution}, for the meaning of +@var{kflag}. Used with the @code{checkout} and @code{update} +commands, your @var{kflag} specification is +@dfn{sticky}; that is, when you use this option +with a @code{checkout} or @code{update} command, +@sc{cvs} associates your selected @var{kflag} with any files +it operates on, and continues to use that @var{kflag} with future +commands on the same files until you specify otherwise. + +The @samp{-k} option is available with the @code{add}, +@code{checkout}, @code{diff}, @code{export}, @code{import} and +@code{update} commands. + +@strong{WARNING: Prior to CVS version 1.12.2, the @samp{-k} flag +overrode the @samp{-kb} indication for a binary file. This could +sometimes corrupt binary files. @xref{Merging and keywords}, for +more.} + +@item -l +Local; run only in current working directory, rather than +recursing through subdirectories. + +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 +@item -m @var{message} +Use @var{message} as log information, instead of +invoking an editor. + +Available with the following commands: @code{add}, +@code{commit} and @code{import}. + +@item -n +Do not run any tag program. (A program can be +specified to run in the modules +database (@pxref{modules}); this option bypasses it). + +@strong{Note: this is not the same as the @samp{cvs -n} +program option, which you can specify to the left of a cvs command!} + +Available with the @code{checkout}, @code{commit}, @code{export}, +and @code{rtag} commands. + +@item -P +Prune empty directories. See @ref{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 -R +Process directories recursively. This is the default for all @sc{cvs} +commands, with the exception of @code{ls} & @code{rls}. + +Available with the following commands: @code{annotate}, @code{checkout}, +@code{commit}, @code{diff}, @code{edit}, @code{editors}, @code{export}, +@code{ls}, @code{rdiff}, @code{remove}, @code{rls}, @code{rtag}, +@code{status}, @code{tag}, @code{unedit}, @code{update}, @code{watch}, +and @code{watchers}. + +@item -r @var{tag} +@cindex HEAD, special tag +@cindex BASE, special tag +Use the revision specified by the @var{tag} argument instead of the +default @dfn{head} revision. As well as arbitrary tags defined +with the @code{tag} or @code{rtag} command, two special tags are +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. + +@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 for the sticky tag, if there is a sticky tag. +@c (b) is ugly as it differs +@c from what HEAD means for other commands, but people +@c and/or scripts are quite possibly used to it. +@c See "head" tests in sanity.sh. +@c Probably the best fix is to introduce two new +@c special tags, ".thead" for the head of the trunk, +@c and ".bhead" for the head of the current branch. +@c Then deprecate HEAD. This has the advantage of +@c not surprising people with a change to HEAD, and a +@c side benefit of also phasing out the poorly-named +@c HEAD (see discussion of reserved tag names in node +@c "Tags"). Of course, .thead and .bhead should be +@c carefully implemented (with the implementation the +@c same for "diff" as for everyone else), test cases +@c written (similar to the ones in "head"), new tests +@c cases written for things like default branches, &c. + +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 +on sticky tags/dates, @pxref{Sticky tags}). + +The tag can be either a symbolic or numeric tag, as +described in @ref{Tags}, or the name of a branch, as +described in @ref{Branching and merging}. + +Specifying the @samp{-q} global option along with the +@samp{-r} command option is often useful, to suppress +the warning messages when the @sc{rcs} file +does not contain the specified tag. + +@strong{Note: this is not the same as the overall @samp{cvs -r} option, +which you can specify to the left of a @sc{cvs} command!} + +@samp{-r} is available with the @code{checkout}, @code{commit}, +@code{diff}, @code{history}, @code{export}, @code{rdiff}, +@code{rtag}, 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. +Available with the following commands: @code{import}, +and @code{update}. + +@end table + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node admin +@appendixsec admin---Administration +@cindex Admin (subcommand) + +@itemize @bullet +@item +Requires: repository, working directory. +@item +Changes: repository. +@item +Synonym: rcs +@end itemize + +This is the @sc{cvs} interface to assorted +administrative facilities. Some of them have +questionable usefulness for @sc{cvs} but exist for +historical purposes. Some of the questionable options +are likely to disappear in the future. This command +@emph{does} work recursively, so extreme care should be +used. + +@cindex cvsadmin +@cindex UserAdminOptions, in CVSROOT/config +On unix, if there is a group named @code{cvsadmin}, +only members of that group can run @code{cvs admin} +commands, except for those specified using the +@code{UserAdminOptions} configuration option in the +@file{CVSROOT/config} file. Options specified using +@code{UserAdminOptions} can be run by any user. See +@ref{config} for more on @code{UserAdminOptions}. + +The @code{cvsadmin} group should exist on the server, +or any system running the non-client/server @sc{cvs}. +To disallow @code{cvs admin} for all users, create a +group with no users in it. On NT, the @code{cvsadmin} +feature does not exist and all users +can run @code{cvs admin}. + +@menu +* admin options:: admin options +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node admin options +@appendixsubsec admin options + +Some of these options have questionable usefulness for +@sc{cvs} but exist for historical purposes. Some even +make it impossible to use @sc{cvs} until you undo the +effect! + +@table @code +@item -A@var{oldfile} +Might not work together with @sc{cvs}. Append the +access list of @var{oldfile} to the access list of the +@sc{rcs} file. + +@item -a@var{logins} +Might not work together with @sc{cvs}. Append the +login names appearing in the comma-separated list +@var{logins} to the access list of the @sc{rcs} file. + +@item -b[@var{rev}] +Set the default branch to @var{rev}. In @sc{cvs}, you +normally do not manipulate default branches; sticky +tags (@pxref{Sticky tags}) are a better way to decide +which branch you want to work on. There is one reason +to run @code{cvs admin -b}: to revert to the vendor's +version when using vendor branches (@pxref{Reverting +local changes}). +There can be no space between @samp{-b} and its argument. +@c Hmm, we don't document the usage where rev is +@c omitted. Maybe that usage can/should be deprecated +@c (and replaced with -bHEAD or something?) (so we can toss +@c the optional argument). Note that -bHEAD does not +@c work, as of 17 Sep 1997, but probably will once "cvs +@c admin" is internal to CVS. + +@cindex Comment leader +@item -c@var{string} +Sets the comment leader to @var{string}. The comment +leader is not used by current versions of @sc{cvs} or +@sc{rcs} 5.7. Therefore, you can almost surely not +worry about it. @xref{Keyword substitution}. + +@item -e[@var{logins}] +Might not work together with @sc{cvs}. Erase the login +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. +There can be no space between @samp{-e} and its argument. + +@item -I +Run interactively, even if the standard input is not a +terminal. This option does not work with the +client/server @sc{cvs} and is likely to disappear in +a future release of @sc{cvs}. + +@item -i +Useless with @sc{cvs}. This creates and initializes a +new @sc{rcs} file, without depositing a revision. With +@sc{cvs}, add files with the @code{cvs add} command +(@pxref{Adding files}). + +@item -k@var{subst} +Set the default keyword +substitution to @var{subst}. @xref{Keyword +substitution}. Giving an explicit @samp{-k} option to +@code{cvs update}, @code{cvs export}, or @code{cvs +checkout} overrides this default. + +@item -l[@var{rev}] +Lock the revision with number @var{rev}. If a branch +is given, lock the latest revision on that branch. If +@var{rev} is omitted, lock the latest revision on the +default branch. There can be no space between +@samp{-l} and its argument. + +This can be used in conjunction with the +@file{rcslock.pl} script in the @file{contrib} +directory of the @sc{cvs} source distribution to +provide reserved checkouts (where only one user can be +editing a given file at a time). See the comments in +that file for details (and see the @file{README} file +in that directory for disclaimers about the unsupported +nature of contrib). According to comments in that +file, locking must set to strict (which is the default). + +@item -L +Set locking to strict. Strict locking means that the +owner of an RCS file is not exempt from locking for +checkin. For use with @sc{cvs}, strict locking must be +set; see the discussion under the @samp{-l} option above. + +@cindex Changing a log message +@cindex Replacing a log message +@cindex Correcting a log message +@cindex Fixing a log message +@cindex Log message, correcting +@item -m@var{rev}:@var{msg} +Replace the log message of revision @var{rev} with +@var{msg}. + +@c The rcs -M option, to suppress sending mail, has never been +@c documented as a cvs admin option. + +@item -N@var{name}[:[@var{rev}]] +Act like @samp{-n}, except override any previous +assignment of @var{name}. For use with magic branches, +see @ref{Magic branch numbers}. + +@item -n@var{name}[:[@var{rev}]] +Associate the symbolic name @var{name} with the branch +or revision @var{rev}. It is normally better to use +@samp{cvs tag} or @samp{cvs rtag} instead. Delete the +symbolic name if both @samp{:} and @var{rev} are +omitted; otherwise, print an error message if +@var{name} is already associated with another number. +If @var{rev} is symbolic, it is expanded before +association. A @var{rev} consisting of a branch number +followed by a @samp{.} stands for the current latest +revision in the branch. A @samp{:} with an empty +@var{rev} stands for the current latest revision on the +default branch, normally the trunk. For example, +@samp{cvs admin -n@var{name}:} associates @var{name} with the +current latest revision of all the RCS files; +this contrasts with @samp{cvs admin -n@var{name}:$} which +associates @var{name} with the revision numbers +extracted from keyword strings in the corresponding +working files. + +@cindex Deleting revisions +@cindex Outdating revisions +@cindex Saving space +@item -o@var{range} +Deletes (@dfn{outdates}) the revisions given by +@var{range}. + +Note that this command can be quite dangerous unless +you know @emph{exactly} what you are doing (for example +see the warnings below about how the +@var{rev1}:@var{rev2} syntax is confusing). + +If you are short on disc this option might help you. +But think twice before using it---there is no way short +of restoring the latest backup to undo this command! +If you delete different revisions than you planned, +either due to carelessness or (heaven forbid) a @sc{cvs} +bug, there is no opportunity to correct the error +before the revisions are deleted. It probably would be +a good idea to experiment on a copy of the repository +first. + +Specify @var{range} in one of the following ways: + +@table @code +@item @var{rev1}::@var{rev2} +Collapse all revisions between rev1 and rev2, so that +@sc{cvs} only stores the differences associated with going +from rev1 to rev2, not intermediate steps. For +example, after @samp{-o 1.3::1.5} one can retrieve +revision 1.3, revision 1.5, or the differences to get +from 1.3 to 1.5, but not the revision 1.4, or the +differences between 1.3 and 1.4. Other examples: +@samp{-o 1.3::1.4} and @samp{-o 1.3::1.3} have no +effect, because there are no intermediate revisions to +remove. + +@item ::@var{rev} +Collapse revisions between the beginning of the branch +containing @var{rev} and @var{rev} itself. The +branchpoint and @var{rev} are left intact. For +example, @samp{-o ::1.3.2.6} deletes revision 1.3.2.1, +revision 1.3.2.5, and everything in between, but leaves +1.3 and 1.3.2.6 intact. + +@item @var{rev}:: +Collapse revisions between @var{rev} and the end of the +branch containing @var{rev}. Revision @var{rev} is +left intact but the head revision is deleted. + +@item @var{rev} +Delete the revision @var{rev}. For example, @samp{-o +1.3} is equivalent to @samp{-o 1.2::1.4}. + +@item @var{rev1}:@var{rev2} +Delete the revisions from @var{rev1} to @var{rev2}, +inclusive, on the same branch. One will not be able to +retrieve @var{rev1} or @var{rev2} or any of the +revisions in between. For example, the command +@samp{cvs admin -oR_1_01:R_1_02 .} is rarely useful. +It means to delete revisions up to, and including, the +tag R_1_02. But beware! If there are files that have not +changed between R_1_02 and R_1_03 the file will have +@emph{the same} numerical revision number assigned to +the tags R_1_02 and R_1_03. So not only will it be +impossible to retrieve R_1_02; R_1_03 will also have to +be restored from the tapes! In most cases you want to +specify @var{rev1}::@var{rev2} instead. + +@item :@var{rev} +Delete revisions from the beginning of the +branch containing @var{rev} up to and including +@var{rev}. + +@item @var{rev}: +Delete revisions from revision @var{rev}, including +@var{rev} itself, to the end of the branch containing +@var{rev}. +@end table + +None of the revisions to be deleted may have +branches or locks. + +If any of the revisions to be deleted have symbolic +names, and one specifies one of the @samp{::} syntaxes, +then @sc{cvs} will give an error and not delete any +revisions. If you really want to delete both the +symbolic names and the revisions, first delete the +symbolic names with @code{cvs tag -d}, then run +@code{cvs admin -o}. If one specifies the +non-@samp{::} syntaxes, then @sc{cvs} will delete the +revisions but leave the symbolic names pointing to +nonexistent revisions. This behavior is preserved for +compatibility with previous versions of @sc{cvs}, but +because it isn't very useful, in the future it may +change to be like the @samp{::} case. + +Due to the way @sc{cvs} handles branches @var{rev} +cannot be specified symbolically if it is a branch. +@xref{Magic branch numbers}, for an explanation. +@c FIXME: is this still true? I suspect not. + +Make sure that no-one has checked out a copy of the +revision you outdate. Strange things will happen if he +starts to edit it and tries to check it back in. For +this reason, this option is not a good way to take back +a bogus commit; commit a new revision undoing the bogus +change instead (@pxref{Merging two revisions}). + +@item -q +Run quietly; do not print diagnostics. + +@item -s@var{state}[:@var{rev}] +Useful with @sc{cvs}. Set the state attribute of the +revision @var{rev} to @var{state}. If @var{rev} is a +branch number, assume the latest revision on that +branch. If @var{rev} is omitted, assume the latest +revision on the default branch. Any identifier is +acceptable for @var{state}. A useful set of states is +@samp{Exp} (for experimental), @samp{Stab} (for +stable), and @samp{Rel} (for released). By default, +the state of a new revision is set to @samp{Exp} when +it is created. The state is visible in the output from +@var{cvs log} (@pxref{log}), and in the +@samp{$@splitrcskeyword{Log}$} and @samp{$@splitrcskeyword{State}$} keywords +(@pxref{Keyword substitution}). Note that @sc{cvs} +uses the @code{dead} state for its own purposes; to +take a file to or from the @code{dead} state use +commands like @code{cvs remove} and @code{cvs add}, not +@code{cvs admin -s}. + +@item -t[@var{file}] +Useful with @sc{cvs}. Write descriptive text from the +contents of the named @var{file} into the RCS file, +deleting the existing text. The @var{file} pathname +may not begin with @samp{-}. The descriptive text can be seen in the +output from @samp{cvs log} (@pxref{log}). +There can be no space between @samp{-t} and its argument. + +If @var{file} is omitted, +obtain the text from standard input, terminated by +end-of-file or by a line containing @samp{.} by itself. +Prompt for the text if interaction is possible; see +@samp{-I}. + +@item -t-@var{string} +Similar to @samp{-t@var{file}}. Write descriptive text +from the @var{string} into the @sc{rcs} file, deleting +the existing text. +There can be no space between @samp{-t} and its argument. + +@c The rcs -T option, do not update last-mod time for +@c minor changes, has never been documented as a +@c cvs admin option. + +@item -U +Set locking to non-strict. Non-strict locking means +that the owner of a file need not lock a revision for +checkin. For use with @sc{cvs}, strict locking must be +set; see the discussion under the @samp{-l} option +above. + +@item -u[@var{rev}] +See the option @samp{-l} above, for a discussion of +using this option with @sc{cvs}. Unlock the revision +with number @var{rev}. If a branch is given, unlock +the latest revision on that branch. If @var{rev} is +omitted, remove the latest lock held by the caller. +Normally, only the locker of a revision may unlock it; +somebody else unlocking a revision breaks the lock. +This causes the original locker to be sent a @code{commit} +notification (@pxref{Getting Notified}). +There can be no space between @samp{-u} and its argument. + +@item -V@var{n} +In previous versions of @sc{cvs}, this option meant to +write an @sc{rcs} file which would be acceptable to +@sc{rcs} version @var{n}, but it is now obsolete and +specifying it will produce an error. +@c Note that -V without an argument has never been +@c documented as a cvs admin option. + +@item -x@var{suffixes} +In previous versions of @sc{cvs}, this was documented +as a way of specifying the names of the @sc{rcs} +files. However, @sc{cvs} has always required that the +@sc{rcs} files used by @sc{cvs} end in @samp{,v}, so +this option has never done anything useful. + +@c The rcs -z option, to specify the timezone, has +@c never been documented as a cvs admin option. +@end table + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node annotate +@appendixsec annotate---What revision modified each line of a file? +@cindex annotate (subcommand) + +@itemize @bullet +@item +Synopsis: annotate [options] files@dots{} +@item +Requires: repository. +@item +Changes: nothing. +@end itemize + +For each file in @var{files}, print the head revision +of the trunk, together with information on the last +modification for each line. + +@menu +* annotate options:: annotate options +* annotate example:: annotate example +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node annotate options +@appendixsubsec annotate options + +These standard options are supported by @code{annotate} +(@pxref{Common options}, for a complete description of +them): + +@table @code +@item -l +Local directory only, no recursion. + +@item -R +Process directories recursively. + +@item -f +Use head revision if tag/date not found. + +@item -F +Annotate binary files. + +@item -r @var{revision} +Annotate file as of specified revision/tag. + +@item -D @var{date} +Annotate file as of specified date. +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node annotate example +@appendixsubsec annotate example + +For example: + +@example +$ cvs annotate ssfile +Annotations for ssfile +*************** +1.1 (mary 27-Mar-96): ssfile line 1 +1.2 (joe 28-Mar-96): ssfile line 2 +@end example + +The file @file{ssfile} currently contains two lines. +The @code{ssfile line 1} line was checked in by +@code{mary} on March 27. Then, on March 28, @code{joe} +added a line @code{ssfile line 2}, without modifying +the @code{ssfile line 1} line. This report doesn't +tell you anything about lines which have been deleted +or replaced; you need to use @code{cvs diff} for that +(@pxref{diff}). + +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 there and 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 checkout +@appendixsec checkout---Check out sources for editing +@cindex checkout (subcommand) +@cindex co (subcommand) + +@itemize @bullet +@item +Synopsis: checkout [options] modules@dots{} +@item +Requires: repository. +@item +Changes: working directory. +@item +Synonyms: co, get +@end itemize + +Create or update a working directory containing copies of the +source files specified by @var{modules}. You must execute +@code{checkout} before using most of the other @sc{cvs} +commands, since most of them operate on your working +directory. + +The @var{modules} are either +symbolic names for some +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. + +@c FIXME: this seems like a very odd place to introduce +@c people to how CVS works. The bit about unreserved +@c checkouts is also misleading as it depends on how +@c things are set up. +Depending on the modules you specify, @code{checkout} may +recursively create directories and populate them with +the appropriate source files. You can then edit these +source files at any time (regardless of whether other +software developers are editing their own copies of the +sources); update them to include new changes applied by +others to the source repository; or commit your work as +a permanent change to the source repository. + +Note that @code{checkout} is used to create +directories. The top-level directory created is always +added to the directory where @code{checkout} is +invoked, and usually has the same name as the specified +module. In the case of a module alias, the created +sub-directory may have a different name, but you can be +sure that it will be a sub-directory, and that +@code{checkout} will show the relative path leading to +each file as it is extracted into your private work +area (unless you specify the @samp{-Q} global option). + +The files created by @code{checkout} are created +read-write, unless the @samp{-r} option to @sc{cvs} +(@pxref{Global options}) is specified, the +@code{CVSREAD} environment variable is specified +(@pxref{Environment variables}), or a watch is in +effect for that file (@pxref{Watches}). + +Note that running @code{checkout} on a directory that was already +built by a prior @code{checkout} is also permitted. +This is similar to specifying the @samp{-d} option +to the @code{update} command in the sense that new +directories that have been created in the repository +will appear in your work area. +However, @code{checkout} takes a module name whereas +@code{update} takes a directory name. Also +to use @code{checkout} this way it must be run from the +top level directory (where you originally ran +@code{checkout} from), so before you run +@code{checkout} to update an existing directory, don't +forget to change your directory to the top level +directory. + +For the output produced by the @code{checkout} command +see @ref{update output}. + +@menu +* checkout options:: checkout options +* checkout examples:: checkout examples +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node checkout options +@appendixsubsec checkout options + +These standard options are supported by @code{checkout} +(@pxref{Common options}, for a complete description of +them): + +@table @code +@item -D @var{date} +Use the most recent revision no later than @var{date}. +This option is sticky, and implies @samp{-P}. See +@ref{Sticky tags}, for more information on sticky tags/dates. + +@item -f +Only useful with the @samp{-D @var{date}} or @samp{-r +@var{tag}} flags. If no matching revision is found, +retrieve the most recent revision (instead of ignoring +the file). + +@item -k @var{kflag} +Process keywords according to @var{kflag}. See +@ref{Keyword substitution}. +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. See @ref{Invoking CVS}, for +more information on the @code{status} command. + +@item -l +Local; run only in current working directory. + +@item -n +Do not run any checkout program (as specified +with the @samp{-o} option in the modules file; +@pxref{modules}). + +@item -P +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. +@end table + +In addition to those, you can use these special command +options with @code{checkout}: + +@table @code +@item -A +Reset any sticky tags, dates, or @samp{-k} options. +See @ref{Sticky tags}, for more information on sticky tags/dates. + +@item -c +Copy the module file, sorted, to the standard output, +instead of creating or modifying any files or +directories in your working directory. + +@item -d @var{dir} +Create a directory called @var{dir} for the working +files, instead of using the module name. In general, +using this flag is equivalent to using @samp{mkdir +@var{dir}; cd @var{dir}} followed by the checkout +command without the @samp{-d} flag. + +There is an important exception, however. It is very +convenient when checking out a single item to have the +output appear in a directory that doesn't contain empty +intermediate directories. In this case @emph{only}, +@sc{cvs} tries to ``shorten'' pathnames to avoid those empty +directories. + +For example, given a module @samp{foo} that contains +the file @samp{bar.c}, the command @samp{cvs co -d dir +foo} will create directory @samp{dir} and place +@samp{bar.c} inside. Similarly, given a module +@samp{bar} which has subdirectory @samp{baz} wherein +there is a file @samp{quux.c}, the command @samp{cvs co +-d dir bar/baz} will create directory @samp{dir} and +place @samp{quux.c} inside. + +Using the @samp{-N} flag will defeat this behavior. +Given the same module definitions above, @samp{cvs co +-N -d dir foo} will create directories @samp{dir/foo} +and place @samp{bar.c} inside, while @samp{cvs co -N -d +dir bar/baz} will create directories @samp{dir/bar/baz} +and place @samp{quux.c} inside. + +@item -j @var{tag} +With two @samp{-j} options, merge changes from the +revision specified with the first @samp{-j} option to +the revision specified with the second @samp{j} option, +into the working directory. + +With one @samp{-j} option, merge changes from the +ancestor revision to the revision specified with the +@samp{-j} option, into the working directory. The +ancestor revision is the common ancestor of the +revision which the working directory is based on, and +the revision specified in the @samp{-j} option. + +In addition, each -j option can contain an optional +date specification which, when used with branches, can +limit the chosen revision to one within a specific +date. An optional date is specified by adding a colon +(:) to the tag: +@samp{-j@var{Symbolic_Tag}:@var{Date_Specifier}}. + +@xref{Branching and merging}. + +@item -N +Only useful together with @samp{-d @var{dir}}. With +this option, @sc{cvs} will not ``shorten'' module paths +in your working directory when you check out a single +module. See the @samp{-d} flag for examples and a +discussion. + +@item -s +Like @samp{-c}, but include the status of all modules, +and sort it by the status string. @xref{modules}, for +info about the @samp{-s} option that is used inside the +modules file to set the module status. +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node checkout examples +@appendixsubsec checkout examples + +Get a copy of the module @samp{tc}: + +@example +$ cvs checkout tc +@end example + +Get a copy of the module @samp{tc} as it looked one day +ago: + +@example +$ cvs checkout -D yesterday tc +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node commit +@appendixsec commit---Check files into the repository +@cindex commit (subcommand) + +@itemize @bullet +@item +Synopsis: commit [-lnRf] [-m 'log_message' | +-F file] [-r revision] [files@dots{}] +@item +Requires: working directory, repository. +@item +Changes: repository. +@item +Synonym: ci +@end itemize + +Use @code{commit} when you want to incorporate changes +from your working source files into the source +repository. + +If you don't specify particular files to commit, all of +the files in your working current directory are +examined. @code{commit} is careful to change in the +repository only those files that you have really +changed. By default (or if you explicitly specify the +@samp{-R} option), files in subdirectories are also +examined and committed if they have changed; you can +use the @samp{-l} option to limit @code{commit} to the +current directory only. + +@code{commit} verifies that the selected files are up +to date with the current revisions in the source +repository; it will notify you, and exit without +committing, if any of the specified files must be made +current first with @code{update} (@pxref{update}). +@code{commit} does not call the @code{update} command +for you, but rather leaves that for you to do when the +time is right. + +When all is well, an editor is invoked to allow you to +enter a log message that will be written to one or more +logging programs (@pxref{modules}, and @pxref{loginfo}) +and placed in the @sc{rcs} file inside the +repository. This log message can be retrieved with the +@code{log} command; see @ref{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 +that the argument file contains the log message. + +@menu +* commit options:: commit options +* commit examples:: commit examples +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node commit options +@appendixsubsec commit options + +These standard options are supported by @code{commit} +(@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. + +@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 +(@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} +Read the log message from @var{file}, instead +of invoking an editor. + +@item -f +Note that this is not the standard behavior of +the @samp{-f} option as defined in @ref{Common options}. + +Force @sc{cvs} to commit a new revision even if you haven't +made any changes to the file. If the current revision +of @var{file} is 1.7, then the following two commands +are equivalent: + +@example +$ cvs commit -f @var{file} +$ cvs commit -r 1.8 @var{file} +@end example + +@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 +invoking an editor. +@end table + +@need 2000 +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node commit examples +@appendixsubsec commit examples + +@c FIXME: this material wants to be somewhere +@c in "Branching and merging". + +@appendixsubsubsec Committing to a branch + +You can commit to a branch revision (one that has an +even number of dots) with the @samp{-r} option. To +create a branch revision, use the @samp{-b} option +of the @code{rtag} or @code{tag} commands +(@pxref{Branching and merging}). Then, either @code{checkout} or +@code{update} can be used to base your sources on the +newly created branch. From that point on, all +@code{commit} changes made within these working sources +will be automatically added to a branch revision, +thereby not disturbing main-line development in any +way. For example, if you had to create a patch to the +1.2 version of the product, even though the 2.0 version +is already under development, you might do: + +@example +$ cvs rtag -b -r FCS1_2 FCS1_2_Patch product_module +$ cvs checkout -r FCS1_2_Patch product_module +$ cd product_module +[[ hack away ]] +$ cvs commit +@end example + +@noindent +This works automatically since the @samp{-r} option is +sticky. + +@appendixsubsubsec Creating the branch after editing + +Say you have been working on some extremely +experimental software, based on whatever revision you +happened to checkout last week. If others in your +group would like to work on this software with you, but +without disturbing main-line development, you could +commit your change to a new branch. Others can then +checkout your experimental stuff and utilize the full +benefit of @sc{cvs} conflict resolution. The scenario might +look like: + +@c FIXME: Should we be recommending tagging the branchpoint? +@example +[[ hacked sources are present ]] +$ cvs tag -b EXPR1 +$ cvs update -r EXPR1 +$ cvs commit +@end example + +The @code{update} command will make the @samp{-r +EXPR1} option sticky on all files. Note that your +changes to the files will never be removed by the +@code{update} command. The @code{commit} will +automatically commit to the correct branch, because the +@samp{-r} is sticky. You could also do like this: + +@c FIXME: Should we be recommending tagging the branchpoint? +@example +[[ hacked sources are present ]] +$ cvs tag -b EXPR1 +$ cvs commit -r EXPR1 +@end example + +@noindent +but then, only those files that were changed by you +will have the @samp{-r EXPR1} sticky flag. If you hack +away, and commit without specifying the @samp{-r EXPR1} +flag, some files may accidentally end up on the main +trunk. + +To work with you on the experimental change, others +would simply do + +@example +$ cvs checkout -r EXPR1 whatever_module +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node diff +@appendixsec diff---Show differences between revisions +@cindex diff (subcommand) + +@itemize @bullet +@item +Synopsis: diff [-lR] [-k kflag] [format_options] [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files@dots{}] +@item +Requires: working directory, repository. +@item +Changes: nothing. +@end itemize + +The @code{diff} command is used to compare different +revisions of files. The default action is to compare +your working files with the revisions they were based +on, and report any differences that are found. + +If any file names are given, only those files are +compared. If any directories are given, all files +under them will be compared. + +The exit status for diff is different than for other +@sc{cvs} commands; for details @ref{Exit status}. + +@menu +* diff options:: diff options +* diff examples:: diff examples +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node diff options +@appendixsubsec diff options + +These standard options are supported by @code{diff} +(@pxref{Common options}, for a complete description of +them): + +@table @code +@item -D @var{date} +Use the most recent revision no later than @var{date}. +See @samp{-r} for how this affects the comparison. + +@item -k @var{kflag} +Process keywords according to @var{kflag}. See +@ref{Keyword substitution}. + +@item -l +Local; run only in current working directory. + +@item -R +Examine directories recursively. This option is on by +default. + +@item -r @var{tag} +Compare with revision @var{tag}. Zero, one or two +@samp{-r} options can be present. With no @samp{-r} +option, the working file will be compared with the +revision it was based on. With one @samp{-r}, that +revision will be compared to your current working file. +With two @samp{-r} options those two revisions will be +compared (and your working file will not affect the +outcome in any way). +@c We should be a lot more explicit, with examples, +@c about the difference between "cvs diff" and "cvs +@c diff -r HEAD". This often confuses new users. + +One or both @samp{-r} options can be replaced by a +@samp{-D @var{date}} option, described above. +@end table + +@c Conceptually, this is a disaster. There are 3 +@c zillion diff formats that we support via the diff +@c library. It is not obvious to me that we should +@c document them all. Maybe just the most common ones +@c like -c and -u, and think about phasing out the +@c obscure ones. +@c FIXCVS: also should be a way to specify an external +@c diff program (which can be different for different +@c file types) and 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 foo" option to diff. +@c This would fit nicely with deprecating/eliminating +@c the obscure options of the diff library, because it +@c would let people specify an external GNU diff if +@c they are into that sort of thing. +The following options specify the format of the +output. They have the same meaning as in GNU diff. +Most options have two equivalent names, one of which is a single letter +preceded by @samp{-}, and the other of which is a long name preceded by +@samp{--}. + +@table @samp +@item -@var{lines} +Show @var{lines} (an integer) lines of context. This option does not +specify an output format by itself; it has no effect unless it is +combined with @samp{-c} or @samp{-u}. This option is obsolete. For proper +operation, @code{patch} typically needs at least two lines of context. + +@item -a +Treat all files as text and compare them line-by-line, even if they +do not seem to be text. + +@item -b +Ignore trailing white space and consider all other sequences of one or +more white space characters to be equivalent. + +@item -B +Ignore changes that just insert or delete blank lines. + +@item --binary +Read and write data in binary mode. + +@item --brief +Report only whether the files differ, not the details of the +differences. + +@item -c +Use the context output format. + +@item -C @var{lines} +@itemx --context@r{[}=@var{lines}@r{]} +Use the context output format, showing @var{lines} (an integer) lines of +context, or three if @var{lines} is not given. +For proper operation, @code{patch} typically needs at least two lines of +context. + +@item --changed-group-format=@var{format} +Use @var{format} to output a line group containing differing lines from +both files in if-then-else format. @xref{Line group formats}. + +@item -d +Change the algorithm to perhaps find a smaller set of changes. This makes +@code{diff} slower (sometimes much slower). + +@item -e +@itemx --ed +Make output that is a valid @code{ed} script. + +@item --expand-tabs +Expand tabs to spaces in the output, to preserve the alignment of tabs +in the input files. + +@item -f +Make output that looks vaguely like an @code{ed} script but has changes +in the order they appear in the file. + +@item -F @var{regexp} +In context and unified format, for each hunk of differences, show some +of the last preceding line that matches @var{regexp}. + +@item --forward-ed +Make output that looks vaguely like an @code{ed} script but has changes +in the order they appear in the file. + +@item -H +Use heuristics to speed handling of large files that have numerous +scattered small changes. + +@item --horizon-lines=@var{lines} +Do not discard the last @var{lines} lines of the common prefix +and the first @var{lines} lines of the common suffix. + +@item -i +Ignore changes in case; consider upper- and lower-case letters +equivalent. + +@item -I @var{regexp} +Ignore changes that just insert or delete lines that match @var{regexp}. + +@item --ifdef=@var{name} +Make merged if-then-else output using @var{name}. + +@item --ignore-all-space +Ignore white space when comparing lines. + +@item --ignore-blank-lines +Ignore changes that just insert or delete blank lines. + +@item --ignore-case +Ignore changes in case; consider upper- and lower-case to be the same. + +@item --ignore-matching-lines=@var{regexp} +Ignore changes that just insert or delete lines that match @var{regexp}. + +@item --ignore-space-change +Ignore trailing white space and consider all other sequences of one or +more white space characters to be equivalent. + +@item --initial-tab +Output a tab rather than a space before the text of a line in normal or +context format. This causes the alignment of tabs in the line to look +normal. + +@item -L @var{label} +Use @var{label} instead of the file name in the context format +and unified format headers. + +@item --label=@var{label} +Use @var{label} instead of the file name in the context format +and unified format headers. + +@item --left-column +Print only the left column of two common lines in side by side format. + +@item --line-format=@var{format} +Use @var{format} to output all input lines in if-then-else format. +@xref{Line formats}. + +@item --minimal +Change the algorithm to perhaps find a smaller set of changes. This +makes @code{diff} slower (sometimes much slower). + +@item -n +Output RCS-format diffs; like @samp{-f} except that each command +specifies the number of lines affected. + +@item -N +@itemx --new-file +In directory comparison, if a file is found in only one directory, +treat it as present but empty in the other directory. + +@item --new-group-format=@var{format} +Use @var{format} to output a group of lines taken from just the second +file in if-then-else format. @xref{Line group formats}. + +@item --new-line-format=@var{format} +Use @var{format} to output a line taken from just the second file in +if-then-else format. @xref{Line formats}. + +@item --old-group-format=@var{format} +Use @var{format} to output a group of lines taken from just the first +file in if-then-else format. @xref{Line group formats}. + +@item --old-line-format=@var{format} +Use @var{format} to output a line taken from just the first file in +if-then-else format. @xref{Line formats}. + +@item -p +Show which C function each change is in. + +@item --rcs +Output RCS-format diffs; like @samp{-f} except that each command +specifies the number of lines affected. + +@item --report-identical-files +@itemx -s +Report when two files are the same. + +@item --show-c-function +Show which C function each change is in. + +@item --show-function-line=@var{regexp} +In context and unified format, for each hunk of differences, show some +of the last preceding line that matches @var{regexp}. + +@item --side-by-side +Use the side by side output format. + +@item --speed-large-files +Use heuristics to speed handling of large files that have numerous +scattered small changes. + +@item --suppress-common-lines +Do not print common lines in side by side format. + +@item -t +Expand tabs to spaces in the output, to preserve the alignment of tabs +in the input files. + +@item -T +Output a tab rather than a space before the text of a line in normal or +context format. This causes the alignment of tabs in the line to look +normal. + +@item --text +Treat all files as text and compare them line-by-line, even if they +do not appear to be text. + +@item -u +Use the unified output format. + +@item --unchanged-group-format=@var{format} +Use @var{format} to output a group of common lines taken from both files +in if-then-else format. @xref{Line group formats}. + +@item --unchanged-line-format=@var{format} +Use @var{format} to output a line common to both files in if-then-else +format. @xref{Line formats}. + +@item -U @var{lines} +@itemx --unified@r{[}=@var{lines}@r{]} +Use the unified output format, showing @var{lines} (an integer) lines of +context, or three if @var{lines} is not given. +For proper operation, @code{patch} typically needs at least two lines of +context. + +@item -w +Ignore white space when comparing lines. + +@item -W @var{columns} +@itemx --width=@var{columns} +Use an output width of @var{columns} in side by side format. + +@item -y +Use the side by side output format. +@end table + +@menu +* Line group formats:: Line group formats +* Line formats:: Line formats +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node Line group formats +@appendixsubsubsec Line group formats + +Line group formats let you specify formats suitable for many +applications that allow if-then-else input, including programming +languages and text formatting languages. A line group format specifies +the output format for a contiguous group of similar lines. + +For example, the following command compares the TeX file @file{myfile} +with the original version from the repository, +and outputs a merged file in which old regions are +surrounded by @samp{\begin@{em@}}-@samp{\end@{em@}} lines, and new +regions are surrounded by @samp{\begin@{bf@}}-@samp{\end@{bf@}} lines. + +@example +cvs diff \ + --old-group-format='\begin@{em@} +%<\end@{em@} +' \ + --new-group-format='\begin@{bf@} +%>\end@{bf@} +' \ + myfile +@end example + +The following command is equivalent to the above example, but it is a +little more verbose, because it spells out the default line group formats. + +@example +cvs diff \ + --old-group-format='\begin@{em@} +%<\end@{em@} +' \ + --new-group-format='\begin@{bf@} +%>\end@{bf@} +' \ + --unchanged-group-format='%=' \ + --changed-group-format='\begin@{em@} +%<\end@{em@} +\begin@{bf@} +%>\end@{bf@} +' \ + myfile +@end example + +Here is a more advanced example, which outputs a diff listing with +headers containing line numbers in a ``plain English'' style. + +@example +cvs diff \ + --unchanged-group-format='' \ + --old-group-format='-------- %dn line%(n=1?:s) deleted at %df: +%<' \ + --new-group-format='-------- %dN line%(N=1?:s) added after %de: +%>' \ + --changed-group-format='-------- %dn line%(n=1?:s) changed at %df: +%<-------- to: +%>' \ + myfile +@end example + +To specify a line group format, use one of the options +listed below. You can specify up to four line group formats, one for +each kind of line group. You should quote @var{format}, because it +typically contains shell metacharacters. + +@table @samp +@item --old-group-format=@var{format} +These line groups are hunks containing only lines from the first file. +The default old group format is the same as the changed group format if +it is specified; otherwise it is a format that outputs the line group as-is. + +@item --new-group-format=@var{format} +These line groups are hunks containing only lines from the second +file. The default new group format is same as the changed group +format if it is specified; otherwise it is a format that outputs the +line group as-is. + +@item --changed-group-format=@var{format} +These line groups are hunks containing lines from both files. The +default changed group format is the concatenation of the old and new +group formats. + +@item --unchanged-group-format=@var{format} +These line groups contain lines common to both files. The default +unchanged group format is a format that outputs the line group as-is. +@end table + +In a line group format, ordinary characters represent themselves; +conversion specifications start with @samp{%} and have one of the +following forms. + +@table @samp +@item %< +stands for the lines from the first file, including the trailing newline. +Each line is formatted according to the old line format (@pxref{Line formats}). + +@item %> +stands for the lines from the second file, including the trailing newline. +Each line is formatted according to the new line format. + +@item %= +stands for the lines common to both files, including the trailing newline. +Each line is formatted according to the unchanged line format. + +@item %% +stands for @samp{%}. + +@item %c'@var{C}' +where @var{C} is a single character, stands for @var{C}. +@var{C} may not be a backslash or an apostrophe. +For example, @samp{%c':'} stands for a colon, even inside +the then-part of an if-then-else format, which a colon would +normally terminate. + +@item %c'\@var{O}' +where @var{O} is a string of 1, 2, or 3 octal digits, +stands for the character with octal code @var{O}. +For example, @samp{%c'\0'} stands for a null character. + +@item @var{F}@var{n} +where @var{F} is a @code{printf} conversion specification and @var{n} is one +of the following letters, stands for @var{n}'s value formatted with @var{F}. + +@table @samp +@item e +The line number of the line just before the group in the old file. + +@item f +The line number of the first line in the group in the old file; +equals @var{e} + 1. + +@item l +The line number of the last line in the group in the old file. + +@item m +The line number of the line just after the group in the old file; +equals @var{l} + 1. + +@item n +The number of lines in the group in the old file; equals @var{l} - @var{f} + 1. + +@item E, F, L, M, N +Likewise, for lines in the new file. + +@end table + +The @code{printf} conversion specification can be @samp{%d}, +@samp{%o}, @samp{%x}, or @samp{%X}, specifying decimal, octal, +lower case hexadecimal, or upper case hexadecimal output +respectively. After the @samp{%} the following options can appear in +sequence: a @samp{-} specifying left-justification; an integer +specifying the minimum field width; and a period followed by an +optional integer specifying the minimum number of digits. +For example, @samp{%5dN} prints the number of new lines in the group +in a field of width 5 characters, using the @code{printf} format @code{"%5d"}. + +@item (@var{A}=@var{B}?@var{T}:@var{E}) +If @var{A} equals @var{B} then @var{T} else @var{E}. +@var{A} and @var{B} are each either a decimal constant +or a single letter interpreted as above. +This format spec is equivalent to @var{T} if +@var{A}'s value equals @var{B}'s; otherwise it is equivalent to @var{E}. + +For example, @samp{%(N=0?no:%dN) line%(N=1?:s)} is equivalent to +@samp{no lines} if @var{N} (the number of lines in the group in the +new file) is 0, to @samp{1 line} if @var{N} is 1, and to @samp{%dN lines} +otherwise. +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node Line formats +@appendixsubsubsec Line formats + +Line formats control how each line taken from an input file is +output as part of a line group in if-then-else format. + +For example, the following command outputs text with a one-column +change indicator to the left of the text. The first column of output +is @samp{-} for deleted lines, @samp{|} for added lines, and a space +for unchanged lines. The formats contain newline characters where +newlines are desired on output. + +@example +cvs diff \ + --old-line-format='-%l +' \ + --new-line-format='|%l +' \ + --unchanged-line-format=' %l +' \ + myfile +@end example + +To specify a line format, use one of the following options. You should +quote @var{format}, since it often contains shell metacharacters. + +@table @samp +@item --old-line-format=@var{format} +formats lines just from the first file. + +@item --new-line-format=@var{format} +formats lines just from the second file. + +@item --unchanged-line-format=@var{format} +formats lines common to both files. + +@item --line-format=@var{format} +formats all lines; in effect, it sets all three above options simultaneously. +@end table + +In a line format, ordinary characters represent themselves; +conversion specifications start with @samp{%} and have one of the +following forms. + +@table @samp +@item %l +stands for the contents of the line, not counting its trailing +newline (if any). This format ignores whether the line is incomplete. + +@item %L +stands for the contents of the line, including its trailing newline +(if any). If a line is incomplete, this format preserves its +incompleteness. + +@item %% +stands for @samp{%}. + +@item %c'@var{C}' +where @var{C} is a single character, stands for @var{C}. +@var{C} may not be a backslash or an apostrophe. +For example, @samp{%c':'} stands for a colon. + +@item %c'\@var{O}' +where @var{O} is a string of 1, 2, or 3 octal digits, +stands for the character with octal code @var{O}. +For example, @samp{%c'\0'} stands for a null character. + +@item @var{F}n +where @var{F} is a @code{printf} conversion specification, +stands for the line number formatted with @var{F}. +For example, @samp{%.5dn} prints the line number using the +@code{printf} format @code{"%.5d"}. @xref{Line group formats}, for +more about printf conversion specifications. + +@end table + +The default line format is @samp{%l} followed by a newline character. + +If the input contains tab characters and it is important that they line +up on output, you should ensure that @samp{%l} or @samp{%L} in a line +format is just after a tab stop (e.g.@: by preceding @samp{%l} or +@samp{%L} with a tab character), or you should use the @samp{-t} or +@samp{--expand-tabs} option. + +Taken together, the line and line group formats let you specify many +different formats. For example, the following command uses a format +similar to @code{diff}'s normal format. You can tailor this command +to get fine control over @code{diff}'s output. + +@example +cvs diff \ + --old-line-format='< %l +' \ + --new-line-format='> %l +' \ + --old-group-format='%df%(f=l?:,%dl)d%dE +%<' \ + --new-group-format='%dea%dF%(F=L?:,%dL) +%>' \ + --changed-group-format='%df%(f=l?:,%dl)c%dF%(F=L?:,%dL) +%<--- +%>' \ + --unchanged-group-format='' \ + myfile +@end example + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node diff examples +@appendixsubsec diff examples + +The following line produces a Unidiff (@samp{-u} flag) +between revision 1.14 and 1.19 of +@file{backend.c}. Due to the @samp{-kk} flag no +keywords are substituted, so differences that only depend +on keyword substitution are ignored. + +@example +$ cvs diff -kk -u -r 1.14 -r 1.19 backend.c +@end example + +Suppose the experimental branch EXPR1 was based on a +set of files tagged RELEASE_1_0. To see what has +happened on that branch, the following can be used: + +@example +$ cvs diff -r RELEASE_1_0 -r EXPR1 +@end example + +A command like this can be used to produce a context +diff between two releases: + +@example +$ cvs diff -c -r RELEASE_1_0 -r RELEASE_1_1 > diffs +@end example + +If you are maintaining ChangeLogs, a command like the following +just before you commit your changes may help you write +the ChangeLog entry. All local modifications that have +not yet been committed will be printed. + +@example +$ cvs diff -u | less +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node export +@appendixsec export---Export sources from CVS, similar to checkout +@cindex export (subcommand) + +@itemize @bullet +@item +Synopsis: export [-flNnR] [-r rev|-D date] [-k subst] [-d dir] module@dots{} +@item +Requires: repository. +@item +Changes: current directory. +@end itemize + +This command is a variant of @code{checkout}; use it +when you want a copy of the source for module without +the @sc{cvs} administrative directories. For example, you +might use @code{export} to prepare source for shipment +off-site. This command requires that you specify a +date or tag (with @samp{-D} or @samp{-r}), so that you +can count on reproducing the source you ship to others +(and thus it always prunes empty directories). + +One often would like to use @samp{-kv} with @code{cvs +export}. This causes any keywords to be +expanded such that an import done at some other site +will not lose the keyword revision information. But be +aware that doesn't handle an export containing binary +files correctly. Also be aware that after having used +@samp{-kv}, one can no longer use the @code{ident} +command (which is part of the @sc{rcs} suite---see +ident(1)) which looks for keyword strings. If +you want to be able to use @code{ident} you must not +use @samp{-kv}. + +@menu +* export options:: export options +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node export options +@appendixsubsec export options + +These standard options are supported by @code{export} +(@pxref{Common options}, for a complete description of +them): + +@table @code +@item -D @var{date} +Use the most recent revision no later than @var{date}. + +@item -f +If no matching revision is found, retrieve the most +recent revision (instead of ignoring the file). + +@item -l +Local; run only in current working directory. + +@item -n +Do not run any checkout program. + +@item -R +Export directories recursively. This is on by default. + +@item -r @var{tag} +Use revision @var{tag}. +@end table + +In addition, these options (that are common to +@code{checkout} and @code{export}) are also supported: + +@table @code +@item -d @var{dir} +Create a directory called @var{dir} for the working +files, instead of using the module name. +@xref{checkout options}, for complete details on how +@sc{cvs} handles this flag. + +@item -k @var{subst} +Set keyword expansion mode (@pxref{Substitution modes}). + +@item -N +Only useful together with @samp{-d @var{dir}}. +@xref{checkout options}, for complete details on how +@sc{cvs} handles this flag. +@end table + +@ignore +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@c @node export examples +@appendixsubsec export examples + +Contributed examples are gratefully accepted. +@c -- Examples here!! +@end ignore + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node history +@appendixsec history---Show status of files and users +@cindex history (subcommand) + +@itemize @bullet +@item +Synopsis: history [-report] [-flags] [-options args] [files@dots{}] +@item +Requires: the file @file{$CVSROOT/CVSROOT/history} +@item +Changes: nothing. +@end itemize + +@sc{cvs} can keep a history file that tracks each use of the +@code{checkout}, @code{commit}, @code{rtag}, +@code{update}, and @code{release} commands. You can +use @code{history} to display this information in +various formats. + +Logging must be enabled by creating the file +@file{$CVSROOT/CVSROOT/history}. + +@strong{Note: @code{history} uses @samp{-f}, @samp{-l}, +@samp{-n}, and @samp{-p} in ways that conflict with the +normal use inside @sc{cvs} (@pxref{Common options}).} + +@menu +* history options:: history options +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node history options +@appendixsubsec history options + +Several options (shown above as @samp{-report}) control what +kind of report is generated: + +@table @code +@item -c +Report on each time commit was used (i.e., each time +the repository was modified). + +@item -e +Everything (all record types). Equivalent to +specifying @samp{-x} with all record types. Of course, +@samp{-e} will also include record types which are +added in a future version of @sc{cvs}; if you are +writing a script which can only handle certain record +types, you'll want to specify @samp{-x}. + +@item -m @var{module} +Report on a particular module. (You can meaningfully +use @samp{-m} more than once on the command line.) + +@item -o +Report on checked-out modules. This is the default report type. + +@item -T +Report on all tags. + +@item -x @var{type} +Extract a particular set of record types @var{type} from the @sc{cvs} +history. The types are indicated by single letters, +which you may specify in combination. + +Certain commands have a single record type: + +@table @code +@item F +release +@item O +checkout +@item E +export +@item T +rtag +@end table + +@noindent +One of five record types may result from an update: + +@table @code +@item C +A merge was necessary but collisions were +detected (requiring manual merging). +@item G +A merge was necessary and it succeeded. +@item U +A working file was copied from the repository. +@item P +A working file was patched to match the repository. +@item W +The working copy of a file was deleted during +update (because it was gone from the repository). +@end table + +@noindent +One of three record types results from commit: + +@table @code +@item A +A file was added for the first time. +@item M +A file was modified. +@item R +A file was removed. +@end table +@end table + +The options shown as @samp{-flags} constrain or expand +the report without requiring option arguments: + +@table @code +@item -a +Show data for all users (the default is to show data +only for the user executing @code{history}). + +@item -l +Show last modification only. + +@item -w +Show only the records for modifications done from the +same working directory where @code{history} is +executing. +@end table + +The options shown as @samp{-options @var{args}} constrain the report +based on an argument: + +@table @code +@item -b @var{str} +Show data back to a record containing the string +@var{str} in either the module name, the file name, or +the repository path. + +@item -D @var{date} +Show data since @var{date}. This is slightly different +from the normal use of @samp{-D @var{date}}, which +selects the newest revision older than @var{date}. + +@item -f @var{file} +Show data for a particular file +(you can specify several @samp{-f} options on the same command line). +This is equivalent to specifying the file on the command line. + +@item -n @var{module} +Show data for a particular module +(you can specify several @samp{-n} options on the same command line). + +@item -p @var{repository} +Show data for a particular source repository (you +can specify several @samp{-p} options on the same command +line). + +@item -r @var{rev} +Show records referring to revisions since the revision +or tag named @var{rev} appears in individual @sc{rcs} +files. Each @sc{rcs} file is searched for the revision or +tag. + +@item -t @var{tag} +Show records since tag @var{tag} was last added to the +history file. This differs from the @samp{-r} flag +above in that it reads only the history file, not the +@sc{rcs} files, and is much faster. + +@item -u @var{name} +Show records for user @var{name}. + +@item -z @var{timezone} +Show times in the selected records using the specified +time zone instead of UTC. +@end table + +@ignore +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@c @node history examples +@appendixsubsec history examples + +Contributed examples will gratefully be accepted. +@c -- Examples here! +@end ignore + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node import +@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{} +@item +Requires: Repository, source distribution directory. +@item +Changes: repository. +@end itemize + +Use @code{import} to incorporate an entire source +distribution from an outside source (e.g., a source +vendor) into your source repository directory. You can +use this command both for initial creation of a +repository, and for wholesale updates to the module +from the outside source. @xref{Tracking sources}, for +a discussion on this subject. + +The @var{repository} argument gives a directory name +(or a path to a directory) under the @sc{cvs} root directory +for repositories; if the directory did not exist, +import creates it. + +When you use import for updates to source that has been +modified in your source repository (since a prior +import), it will notify you of any files that conflict +in the two branches of development; use @samp{checkout +-j} to reconcile the differences, as import instructs +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 (@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 +file will be treated as packages and the appropriate +filtering will be performed on the file/directory +before being imported. @xref{Wrappers}. + +The outside source is saved in a first-level +branch, by default 1.1.1. Updates are leaves of this +branch; for example, files from the first imported +collection of source will be revision 1.1.1.1, then +files from the first imported update will be revision +1.1.1.2, and so on. + +At least three arguments are required. +@var{repository} is needed to identify the collection +of source. @var{vendortag} is a tag for the entire +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 + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node import options +@appendixsubsec import options + +This standard option is supported by @code{import} +(@pxref{Common options}, for a complete description): + +@table @code +@item -m @var{message} +Use @var{message} as log information, instead of +invoking an editor. +@end table + +There are the following additional special options. + +@table @code +@item -b @var{branch} +See @ref{Multiple vendor branches}. + +@item -k @var{subst} +Indicate the 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 +list of valid @samp{-k} settings. + +@item -I @var{name} +Specify file names that should be ignored during +import. You can use this option repeatedly. To avoid +ignoring any files at all (even those ignored by +default), specify `-I !'. + +@var{name} can be a file name pattern of the same type +that you can specify in the @file{.cvsignore} file. +@xref{cvsignore}. +@c -- Is this really true? + +@item -W @var{spec} +Specify file names that should be filtered during +import. You can use this option repeatedly. + +@var{spec} can be a file name pattern of the same type +that you can specify in the @file{.cvswrappers} +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 is not 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 + +See @ref{Tracking sources}, and @ref{From files}. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node log +@appendixsec log---Print out log information for files +@cindex log (subcommand) + +@itemize @bullet +@item +Synopsis: log [options] [files@dots{}] +@item +Requires: repository, working directory. +@item +Changes: nothing. +@end itemize + +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 +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 +date, the +author, the number of lines added/deleted and the log +message are printed. All dates are displayed in local +time at the client. This is typically specified in the +@code{$TZ} environment variable, which can be set to +govern how @code{log} displays dates. + +@strong{Note: @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 +* log examples:: log examples +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node log options +@appendixsubsec log options + +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} +Print information about revisions with a checkin +date/time in the range given by the +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}. + +@item <@var{d} +@itemx @var{d}> +Select all revisions dated @var{d} or earlier. + +@item @var{d}< +@itemx >@var{d} +Select all revisions dated @var{d} or later. + +@item @var{d} +Select the single, latest revision dated @var{d} or +earlier. +@end table + +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 name of the @sc{rcs} file, name +of the file in the working directory, 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 +tags, so rather than "more"'ing over 3 pages of tag +information, the log information is presented without +tags at all. + +@item -R +Print only the name of the @sc{rcs} file. + +@c Note that using a bare revision (in addition to not +@c being explicitly documented here) is potentially +@c confusing; it shows the log message to get from the +@c previous revision to that revision. "-r1.3 -r1.6" +@c (equivalent to "-r1.3,1.6") is even worse; it +@c prints the messages to get from 1.2 to 1.3 and 1.5 +@c to 1.6. By analogy with "cvs diff", users might +@c expect that it is more like specifying a range. +@c It is not 100% clear to me how much of this should +@c be documented (for example, multiple -r options +@c perhaps could/should be deprecated given the false +@c analogy with "cvs diff"). +@c In general, this section should be rewritten to talk +@c about messages to get from revision rev1 to rev2, +@c rather than messages for revision rev2 (that is, the +@c messages are associated with a change not a static +@c revision and failing to make this distinction causes +@c much confusion). +@item -r@var{revisions} +Print information about revisions given in the +comma-separated list @var{revisions} of revisions and +ranges. The following table explains the available +range formats: + +@table @code +@item @var{rev1}:@var{rev2} +Revisions @var{rev1} to @var{rev2} (which must be on +the same branch). + +@item @var{rev1}::@var{rev2} +The same, but excluding @var{rev1}. + +@item :@var{rev} +@itemx ::@var{rev} +Revisions from the beginning of the branch up to +and including @var{rev}. + +@item @var{rev}: +Revisions starting with @var{rev} to the end of the +branch containing @var{rev}. + +@item @var{rev}:: +Revisions starting just after @var{rev} to the end of the +branch containing @var{rev}. + +@item @var{branch} +An argument that is a branch means all revisions on +that branch. + +@item @var{branch1}:@var{branch2} +@itemx @var{branch1}::@var{branch2} +A range of branches means all revisions +on the branches in that range. + +@item @var{branch}. +The latest revision in @var{branch}. +@end table + +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 +Suppress the header if no revisions are selected. + +@item -s @var{states} +Print information about revisions whose state +attributes match one of the states given in the +comma-separated list @var{states}. + +@item -t +Print the same as @samp{-h}, plus the descriptive text. + +@item -w@var{logins} +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. There can be no space between the +@samp{-w} option and its argument. +@end table + +@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 +@appendixsubsec log examples + +@cindex Timezone, in output +@cindex Zone, time, in output +Since @code{log} shows dates in local time, +you might want to see them in Coordinated Universal Time (UTC) or +some other timezone. +To do this you can set your @code{$TZ} environment +variable before invoking @sc{cvs}: + +@example +$ TZ=UTC cvs log foo.c +$ TZ=EST cvs log bar.c +@end example + +(If you are using a @code{csh}-style shell, like @code{tcsh}, +you would need to prefix the examples above with @code{env}.) + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node ls & rls +@appendixsec ls & rls +@cindex ls (subcommand) +@cindex rls (subcommand) + +@itemize @bullet +@item +ls [-e | -l] [-RP] [-r revision] [-D date] [path@dots{}] +@item +Requires: repository for @code{rls}, repository & working directory for +@code{ls}. +@item +Changes: nothing. +@item +Synonym: @code{dir} & @code{list} are synonyms for @code{ls} and @code{rdir} +& @code{rlist} are synonyms for @code{rls}. +@end itemize + +The @code{ls} and @code{rls} commands are used to list +files and directories in the repository. + +By default @code{ls} lists the files and directories +that belong in your working directory, what would be +there after an @code{update}. + +By default @code{rls} lists the files and directories +on the tip of the trunk in the topmost directory of the +repository. + +Both commands accept an optional list of file and +directory names, relative to the working directory for +@code{ls} and the topmost directory of the repository +for @code{rls}. Neither is recursive by default. + +@menu +* ls & rls options:: ls & rls options +* rls examples: rls examples +@end menu + +@node ls & rls options +@appendixsubsec ls & rls options + +These standard options are supported by @code{ls} & @code{rls}: + +@table @code +@item -d +Show dead revisions (with tag when specified). + +@item -e +Display in CVS/Entries format. This format is meant to remain easily parsable +by automation. + +@item -l +Display all details. + +@item -P +Don't list contents of empty directories when recursing. + +@item -R +List recursively. + +@item -r @var{revision} +Show files with revision or tag. + +@item -D @var{date} +Show files from date. +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node rls examples +@appendixsubsec rls examples + +@example +$ cvs rls +cvs rls: Listing module: `.' +CVSROOT +first-dir +@end example + +@example +$ cvs rls CVSROOT +cvs rls: Listing module: `CVSROOT' +checkoutlist +commitinfo +config +cvswrappers +loginfo +modules +notify +rcsinfo +taginfo +verifymsg + +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node rdiff +@appendixsec rdiff---'patch' format diffs between releases +@cindex rdiff (subcommand) + +@itemize @bullet +@item +rdiff [-flags] [-V vn] [-r t|-D d [-r t2|-D d2]] modules@dots{} +@item +Requires: repository. +@item +Changes: nothing. +@item +Synonym: patch +@end itemize + +Builds a Larry Wall format patch(1) file between two +releases, that can be fed directly into the @code{patch} +program to bring an old release up-to-date with the new +release. (This is one of the few @sc{cvs} commands that +operates directly from the repository, and doesn't +require a prior checkout.) The diff output is sent to +the standard output device. + +You can specify (using the standard @samp{-r} and +@samp{-D} options) any combination of one or two +revisions or dates. If only one revision or date is +specified, the patch file reflects differences between +that revision or date and the current head revisions in +the @sc{rcs} file. + +Note that if the software release affected is contained +in more than one directory, then it may be necessary to +specify the @samp{-p} option to the @code{patch} command when +patching the old sources, so that @code{patch} is able to find +the files that are located in other directories. + +@menu +* rdiff options:: rdiff options +* rdiff examples:: rdiff examples +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node rdiff options +@appendixsubsec rdiff options + +These standard options are supported by @code{rdiff} +(@pxref{Common options}, for a complete description of +them): + +@table @code +@item -D @var{date} +Use the most recent revision no later than @var{date}. + +@item -f +If no matching revision is found, retrieve the most +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 + +In addition to the above, these options are available: + +@table @code +@item -c +Use the context diff format. This is the default format. + +@item -s +Create a summary change report instead of a patch. The +summary includes information about files that were +changed or added between the releases. It is sent to +the standard output device. This is useful for finding +out, for example, which files have changed between two +dates or revisions. + +@item -t +A diff of the top two revisions is sent to the standard +output device. This is most useful for seeing what the +last change to a file was. + +@item -u +Use the unidiff format for the context diffs. +Remember that old versions +of the @code{patch} program can't handle the unidiff +format, so if you plan to post this patch to the net +you should probably not use @samp{-u}. + +@item -V @var{vn} +Expand keywords according to the rules current in +@sc{rcs} version @var{vn} (the expansion format changed with +@sc{rcs} version 5). Note that this option is no +longer accepted. @sc{cvs} will always expand keywords the +way that @sc{rcs} version 5 does. +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node rdiff examples +@appendixsubsec rdiff examples + +Suppose you receive mail from @t{foo@@example.net} asking for an +update from release 1.2 to 1.4 of the tc compiler. You +have no such patches on hand, but with @sc{cvs} that can +easily be fixed with a command such as this: + +@example +$ cvs rdiff -c -r FOO1_2 -r FOO1_4 tc | \ +$$ Mail -s 'The patches you asked for' foo@@example.net +@end example + +Suppose you have made release 1.3, and forked a branch +called @samp{R_1_3fix} for bug fixes. @samp{R_1_3_1} +corresponds to release 1.3.1, which was made some time +ago. Now, you want to see how much development has been +done on the branch. This command can be used: + +@example +$ cvs patch -s -r R_1_3_1 -r R_1_3fix module-name +cvs rdiff: Diffing module-name +File ChangeLog,v changed from revision 1.52.2.5 to 1.52.2.6 +File foo.c,v changed from revision 1.52.2.3 to 1.52.2.4 +File bar.h,v changed from revision 1.29.2.1 to 1.2 +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node release +@appendixsec release---Indicate that a Module is no longer in use +@cindex release (subcommand) + +@itemize @bullet +@item +release [-d] directories@dots{} +@item +Requires: Working directory. +@item +Changes: Working directory, history log. +@end itemize + +This command is meant to safely cancel the effect of +@samp{cvs checkout}. Since @sc{cvs} doesn't lock files, it +isn't strictly necessary to use this command. You can +always simply delete your working directory, if you +like; but you risk losing changes you may have +forgotten, and you leave no trace in the @sc{cvs} history +file (@pxref{history file}) that you've abandoned your +checkout. + +Use @samp{cvs release} to avoid these problems. This +command checks that no uncommitted changes are +present; that you are executing it from immediately +above a @sc{cvs} working directory; and that the repository +recorded for your files is the same as the repository +defined in the module database. + +If all these conditions are true, @samp{cvs release} +leaves a record of its execution (attesting to your +intentionally abandoning your checkout) in the @sc{cvs} +history log. + +@menu +* release options:: release options +* release output:: release output +* release examples:: release examples +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node release options +@appendixsubsec release options + +The @code{release} command supports one command option: + +@table @code +@item -d +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 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{Adding files}) will be silently deleted---even +if it is non-empty!} +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node release output +@appendixsubsec release output + +Before @code{release} releases your sources it will +print a one-line message for any file that is not +up-to-date. + +@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 (@samp{U} and @samp{P} mean the same thing). + +@item A @var{file} +The file has been added to your private copy of the +sources, but has not yet been committed to the +repository. If you delete your copy of the sources +this file will be lost. + +@item R @var{file} +The file has been removed from your private copy of the +sources, but has not yet been removed from the +repository, since you have not yet committed the +removal. @xref{commit}. + +@item M @var{file} +The file is modified in your working directory. There +might also be a newer revision inside the repository. + +@item ? @var{file} +@var{file} is in your working directory, but does not +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}). If you remove your working +sources, this file will be lost. +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node release examples +@appendixsubsec release examples + +Release the @file{tc} directory, and delete your local working copy +of the files. + +@example +$ cd .. # @r{You must stand immediately above the} + # @r{sources when you issue @samp{cvs release}.} +$ cvs release -d tc +You have [0] altered files in this repository. +Are you sure you want to release (and delete) directory `tc': y +$ +@end example + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node update +@appendixsec update---Bring work tree in sync with repository +@cindex update (subcommand) + +@itemize @bullet +@item +update [-ACdflPpR] [-I name] [-j rev [-j rev]] [-k kflag] [-r tag|-D date] [-W spec] files@dots{} +@item +Requires: repository, working directory. +@item +Changes: working directory. +@end itemize + +After you've run checkout to create your private copy +of source from the common repository, other developers +will continue changing the central source. From time +to time, when it is convenient in your development +process, you can use the @code{update} command from +within your working directory to reconcile your work +with any revisions applied to the source repository +since your last checkout or update. Without the @code{-C} +option, @code{update} will also merge any differences +between the local copy of files and their base revisions +into any destination revisions specified with @code{-r}, +@code{-D}, or @code{-A}. + +@menu +* update options:: update options +* update output:: update output +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node update options +@appendixsubsec update options + +These standard options are available with @code{update} +(@pxref{Common options}, for a complete description of +them): + +@table @code +@item -D date +Use the most recent revision no later than @var{date}. +This option is sticky, and implies @samp{-P}. +See @ref{Sticky tags}, for more information on sticky tags/dates. + +@item -f +Only useful with the @samp{-D @var{date}} or @samp{-r +@var{tag}} flags. If no matching revision is found, +retrieve the most recent revision (instead of ignoring +the file). + +@item -k @var{kflag} +Process keywords according to @var{kflag}. See +@ref{Keyword substitution}. +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. 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. See @ref{Moving directories}. + +@item -p +Pipe files to the standard output. + +@item -R +Update directories recursively (default). @xref{Recursive +behavior}. + +@item -r rev +Retrieve revision/tag @var{rev}. This option is sticky, +and implies @samp{-P}. +See @ref{Sticky tags}, for more information on sticky tags/dates. +@end table + +@need 800 +These special options are also available with +@code{update}. + +@table @code +@item -A +Reset any sticky tags, dates, or @samp{-k} options. +See @ref{Sticky tags}, for more information on sticky tags/dates. + +@item -C +Overwrite locally modified files with clean copies from +the repository (the modified file is saved in +@file{.#@var{file}.@var{revision}}, however). + +@item -d +Create any directories that exist in the repository if +they're missing from the working directory. Normally, +@code{update} acts only on directories and files that +were already enrolled in your working directory. + +This is useful for updating directories that were +created in the repository since the initial checkout; +but it has an unfortunate side effect. If you +deliberately avoided certain directories in the +repository when you created your working directory +(either through use of a module name or by listing +explicitly the files and directories you wanted on the +command line), then updating with @samp{-d} will create +those directories, which may not be what you want. + +@item -I @var{name} +Ignore files whose names match @var{name} (in your +working directory) during the update. You can specify +@samp{-I} more than once on the command line to specify +several files to ignore. Use @samp{-I !} to avoid +ignoring any files at all. @xref{cvsignore}, for other +ways to make @sc{cvs} ignore some files. + +@item -W@var{spec} +Specify file names that should be filtered during +update. You can use this option repeatedly. + +@var{spec} can be a file name pattern of the same type +that you can specify in the @file{.cvswrappers} +file. @xref{Wrappers}. + +@item -j@var{revision} +With two @samp{-j} options, merge changes from the +revision specified with the first @samp{-j} option to +the revision specified with the second @samp{j} option, +into the working directory. + +With one @samp{-j} option, merge changes from the +ancestor revision to the revision specified with the +@samp{-j} option, into the working directory. The +ancestor revision is the common ancestor of the +revision which the working directory is based on, and +the revision specified in the @samp{-j} option. + +Note that using a single @samp{-j @var{tagname}} option rather than +@samp{-j @var{branchname}} to merge changes from a branch will +often not remove files which were removed on the branch. +@xref{Merging adds and removals}, for more. + +In addition, each @samp{-j} option can contain an optional +date specification which, when used with branches, can +limit the chosen revision to one within a specific +date. An optional date is specified by adding a colon +(:) to the tag: +@samp{-j@var{Symbolic_Tag}:@var{Date_Specifier}}. + +@xref{Branching and merging}. + +@end table + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node update output +@appendixsubsec update output + +@code{update} and @code{checkout} keep you informed of +their 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 was brought up to date with respect to the +repository. This is done for any file that exists in +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. This accomplishes the same thing as @samp{U} using less bandwidth. + +@item A @var{file} +The file has been added to your private copy of the +sources, and will be added to the source repository +when you run @code{commit} on the file. This is a +reminder to you that the file needs to be committed. + +@item R @var{file} +The file has been removed from your private copy of the +sources, and will be removed from the source repository +when you run @code{commit} on the file. This is a +reminder to you that the file needs to be committed. + +@item M @var{file} +The file is modified in your working directory. + +@samp{M} can indicate one of two states for a file +you're working on: either there were no modifications +to the same file in the repository, so that your file +remains as you last saw it; or there were modifications +in the repository as well as in your copy, but they +were merged successfully, without conflict, in your +working directory. + +@sc{cvs} will print some messages if it merges your work, +and a backup copy of your working file (as it looked +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 +directory) is now the result of attempting to merge +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 revision that your modified file started +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.) 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 +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 + +@c ----- END MAN 1 ----- +@c --------------------------------------------------------------------- +@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. For other references run the +@code{cvs --help} command, or see @ref{Index}. + +A @sc{cvs} command looks like: + +@example +cvs [ @var{global_options} ] @var{command} [ @var{command_options} ] [ @var{command_args} ] +@end example + +Global options: + +@table @code +@item --allow-root=@var{rootdir} +Specify legal @sc{cvsroot} directory (server only) (not +in @sc{cvs} 1.9 and older). See @ref{Password +authentication server}. + +@item -a +Authenticate all communication (client only) (not in @sc{cvs} +1.9 and older). See @ref{Global options}. + +@item -b +Specify RCS location (@sc{cvs} 1.9 and older). See +@ref{Global options}. + +@item -d @var{root} +Specify the @sc{cvsroot}. See @ref{Repository}. + +@item -e @var{editor} +Edit messages with @var{editor}. See @ref{Committing +your changes}. + +@item -f +Do not read the @file{~/.cvsrc} file. See @ref{Global +options}. + +@item -H +@itemx --help +Print a help message. See @ref{Global options}. + +@item -n +Do not change any files. See @ref{Global options}. + +@item -Q +Be really quiet. See @ref{Global options}. + +@item -q +Be somewhat quiet. See @ref{Global options}. + +@item -r +Make new working files read-only. See @ref{Global options}. + +@item -s @var{variable}=@var{value} +Set a user variable. See @ref{Variables}. + +@item -T @var{tempdir} +Put temporary files in @var{tempdir}. See @ref{Global +options}. + +@item -t +Trace @sc{cvs} execution. See @ref{Global options}. + +@item -v +@item --version +Display version and copyright information for @sc{cvs}. + +@item -w +Make new working files read-write. See @ref{Global +options}. + +@item -x +Encrypt all communication (client only). +See @ref{Global options}. + +@item -z @var{gzip-level} +@cindex Compression +@cindex Gzip +Set the compression level (client only). +See @ref{Global options}. +@end table + +Keyword expansion modes (@pxref{Substitution modes}): + +@example +-kkv $@splitrcskeyword{Id}: file1,v 1.1 1993/12/09 03:21:13 joe Exp $ +-kkvl $@splitrcskeyword{Id}: file1,v 1.1 1993/12/09 03:21:13 joe Exp harry $ +-kk $@splitrcskeyword{Id}$ +-kv file1,v 1.1 1993/12/09 03:21:13 joe Exp +-ko @i{no expansion} +-kb @i{no expansion, file is binary} +@end example + +Keywords (@pxref{Keyword list}): + +@example +$@splitrcskeyword{Author}: joe $ +$@splitrcskeyword{Date}: 1993/12/09 03:21:13 $ +$@splitrcskeyword{CVSHeader}: files/file1,v 1.1 1993/12/09 03:21:13 joe Exp harry $ +$@splitrcskeyword{Header}: /home/files/file1,v 1.1 1993/12/09 03:21:13 joe Exp harry $ +$@splitrcskeyword{Id}: file1,v 1.1 1993/12/09 03:21:13 joe Exp harry $ +$@splitrcskeyword{Locker}: harry $ +$@splitrcskeyword{Name}: snapshot_1_14 $ +$@splitrcskeyword{RCSfile}: file1,v $ +$@splitrcskeyword{Revision}: 1.1 $ +$@splitrcskeyword{Source}: /home/files/file1,v $ +$@splitrcskeyword{State}: Exp $ +$@splitrcskeyword{Log}: file1,v $ +Revision 1.1 1993/12/09 03:30:17 joe +Initial revision + +@end example + +@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. +Commands, command options, and command arguments: + +@table @code +@c ------------------------------------------------------------ +@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 ------------------------------------------------------------ +@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. See @ref{Reverting local changes}. + +@item -c@var{string} +Set comment leader. + +@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 repository. See +@ref{admin options}. + +@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 + +@c ------------------------------------------------------------ +@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 +Force annotation of binary files. (Without this option, +binary files are skipped with a message.) + +@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 + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@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 @var{rev1}/@var{date1} against @var{rev2}. See @ref{diff options}. +@end table + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@item export [@var{options}] @var{modules}@dots{} +Export files from @sc{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 -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{tag} +Checkout revision @var{tag}. See @ref{Common options}. +@end table + +@c ------------------------------------------------------------ +@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 -p @var{repository} +In @var{repository}. 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{TOEFWUPCGMAR}. See @ref{history options}. + +@item -z @var{zone} +Output for time zone @var{zone}. See @ref{history options}. +@end table + +@c ------------------------------------------------------------ +@item import [@var{options}] @var{repository} @var{vendor-tag} @var{release-tags}@dots{} +Import files into @sc{cvs}, using vendor branches. See +@ref{import}. + +@table @code +@item -b @var{bra} +Import to vendor branch @var{bra}. See +@ref{Multiple vendor branches}. + +@item -d +Use the file's modification time as the time of +import. See @ref{import options}. + +@item -k @var{kflag} +Set default 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 + +@c ------------------------------------------------------------ +@item init +Create a @sc{cvs} repository if it doesn't exist. See +@ref{Creating a repository}. + +@c ------------------------------------------------------------ +@item kserver +Kerberos authenticated server. +See @ref{Kerberos authenticated}. + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@item login +Prompt for password for authenticating server. See +@ref{Password authentication client}. + +@c ------------------------------------------------------------ +@item logout +Remove stored password for authenticating server. See +@ref{Password authentication client}. + +@c ------------------------------------------------------------ +@item pserver +Password authenticated server. +See @ref{Password authentication server}. + +@c ------------------------------------------------------------ +@item rannotate [@var{options}] [@var{modules}@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 +Force annotation of binary files. (Without this option, +binary files are skipped with a message.) + +@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 + +@c ------------------------------------------------------------ +@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 (obsolete). See +@ref{rdiff options}. +@end table + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@item rlog [@var{options}] [@var{files}@dots{}] +Print out history information for modules. 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 + +@c ------------------------------------------------------------ +@item rtag [@var{options}] @var{tag} @var{modules}@dots{} +Add a symbolic tag to a module. +See @ref{Revisions} and @ref{Branching and merging}. + +@table @code +@item -a +Clear tag from removed files that would not otherwise +be tagged. See @ref{Tagging add/remove}. + +@item -b +Create a branch named @var{tag}. See @ref{Branching and merging}. + +@item -B +Used in conjunction with -F or -d, enables movement and deletion of +branch tags. Use with extreme caution. + +@item -D @var{date} +Tag revisions as of @var{date}. See @ref{Tagging by date/tag}. + +@item -d +Delete @var{tag}. See @ref{Modifying tags}. + +@item -F +Move @var{tag} if it already exists. See @ref{Modifying tags}. + +@item -f +Force a head revision match if tag/date not found. +See @ref{Tagging by date/tag}. + +@item -l +Local; run only in current working directory. See @ref{Recursive behavior}. + +@item -n +No execution of tag program. See @ref{Common options}. + +@item -R +Operate recursively (default). @xref{Recursive +behavior}. + +@item -r @var{rev} +Tag existing tag @var{rev}. See @ref{Tagging by date/tag}. +@end table + +@c ------------------------------------------------------------ +@item server +Rsh server. See @ref{Connecting via rsh}. + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@item tag [@var{options}] @var{tag} [@var{files}@dots{}] +Add a symbolic tag to checked out version of files. +See @ref{Revisions} and @ref{Branching and merging}. + +@table @code +@item -b +Create a branch named @var{tag}. See @ref{Branching and merging}. + +@item -c +Check that working files are unmodified. See +@ref{Tagging the working directory}. + +@item -D @var{date} +Tag revisions as of @var{date}. See @ref{Tagging by date/tag}. + +@item -d +Delete @var{tag}. See @ref{Modifying tags}. + +@item -F +Move @var{tag} if it already exists. See @ref{Modifying tags}. + +@item -f +Force a head revision match if tag/date not found. +See @ref{Tagging by date/tag}. + +@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} +Tag existing tag @var{rev}. See @ref{Tagging by date/tag}. +@end table + +@c ------------------------------------------------------------ +@item unedit [@var{options}] [@var{files}@dots{}] +Undo an edit command. See @ref{Editing files}. + +@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 + +@c ------------------------------------------------------------ +@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 -C +Overwrite locally modified files with clean copies from +the repository (the modified file is saved in +@file{.#@var{file}.@var{revision}}, however). + +@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 + +@c ------------------------------------------------------------ +@item version +@cindex version (subcommand) + +Display the version of @sc{cvs} being used. If the repository +is remote, display both the client and server versions. + +@c ------------------------------------------------------------ +@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 + +@c ------------------------------------------------------------ +@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 Administrative files +@cindex Administrative files (reference) +@cindex Files, reference manual +@cindex Reference manual (files) +@cindex CVSROOT (file) + +@c FIXME? Somewhere there needs to be a more "how-to" +@c guide to writing these. I think the triggers +@c (commitinfo, loginfo, taginfo, &c) are perhaps a +@c different case than files like modules. One +@c particular issue that people sometimes are +@c (unnecessarily?) worried about is performance, and +@c the impact of writing in perl or sh or ____. +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. For a +discussion of how to edit them, see @ref{Intro +administrative files}. + +The most important of these files is the @file{modules} +file, which defines the modules inside the repository. + +@menu +* modules:: Defining modules +* Wrappers:: Specify binary-ness based on file name +* commit files:: The commit support files (commitinfo, + verifymsg, loginfo) +* taginfo:: Verifying/Logging tags +* rcsinfo:: Templates for the log messages +* cvsignore:: Ignoring files via cvsignore +* checkoutlist:: Adding your own administrative files +* history file:: History information +* Variables:: Various variables are expanded +* config:: Miscellaneous CVS configuration +@end menu + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node modules +@appendixsec The modules file +@cindex Modules (admin file) +@cindex Defining modules (reference manual) + +The @file{modules} file records your definitions of +names for collections of source code. @sc{cvs} will +use these definitions if you use @sc{cvs} to update the +modules file (use normal commands like @code{add}, +@code{commit}, etc). + +The @file{modules} file may contain blank lines and +comments (lines beginning with @samp{#}) as well as +module definitions. Long lines can be continued on the +next line by specifying a backslash (@samp{\}) as the +last character on the line. + +There are three basic types of modules: alias modules, +regular modules, and ampersand modules. The difference +between them is the way that they map files in the +repository to files in the working directory. In all +of the following examples, the top-level repository +contains a directory called @file{first-dir}, which +contains two files, @file{file1} and @file{file2}, and a +directory @file{sdir}. @file{first-dir/sdir} contains +a file @file{sfile}. + +@c FIXME: should test all the examples in this section. + +@menu +* Alias modules:: The simplest kind of module +* Regular modules:: +* Ampersand modules:: +* Excluding directories:: Excluding directories from a module +* Module options:: Regular and ampersand modules can take options +* Module program options:: How the modules ``program options'' programs + are run. +@end menu + +@node Alias modules +@appendixsubsec Alias modules +@cindex Alias modules +@cindex -a, in modules file + +Alias modules are the simplest kind of module: + +@table @code +@item @var{mname} -a @var{aliases}@dots{} +This represents the simplest way of defining a module +@var{mname}. The @samp{-a} flags the definition as a +simple alias: @sc{cvs} will treat any use of @var{mname} (as +a command argument) as if the list of names +@var{aliases} had been specified instead. +@var{aliases} may contain either other module names or +paths. When you use paths in aliases, @code{checkout} +creates all intermediate directories in the working +directory, just as if the path had been specified +explicitly in the @sc{cvs} arguments. +@end table + +For example, if the modules file contains: + +@example +amodule -a first-dir +@end example + +@noindent +then the following two commands are equivalent: + +@example +$ cvs co amodule +$ cvs co first-dir +@end example + +@noindent +and they each would provide output such as: + +@example +cvs checkout: Updating first-dir +U first-dir/file1 +U first-dir/file2 +cvs checkout: Updating first-dir/sdir +U first-dir/sdir/sfile +@end example + +@node Regular modules +@appendixsubsec Regular modules +@cindex Regular modules + +@table @code +@item @var{mname} [ options ] @var{dir} [ @var{files}@dots{} ] +In the simplest case, this form of module definition +reduces to @samp{@var{mname} @var{dir}}. This defines +all the files in directory @var{dir} as module mname. +@var{dir} is a relative path (from @code{$CVSROOT}) to a +directory of source in the source repository. In this +case, on checkout, a single directory called +@var{mname} is created as a working directory; no +intermediate directory levels are used by default, even +if @var{dir} was a path involving several directory +levels. +@end table + +For example, if a module is defined by: + +@example +regmodule first-dir +@end example + +@noindent +then regmodule will contain the files from first-dir: + +@example +$ cvs co regmodule +cvs checkout: Updating regmodule +U regmodule/file1 +U regmodule/file2 +cvs checkout: Updating regmodule/sdir +U regmodule/sdir/sfile +$ +@end example + +By explicitly specifying files in the module definition +after @var{dir}, you can select particular files from +directory @var{dir}. Here is +an example: + +@example +regfiles first-dir/sdir sfile +@end example + +@noindent +With this definition, getting the regfiles module +will create a single working directory +@file{regfiles} containing the file listed, which +comes from a directory deeper +in the @sc{cvs} source repository: + +@example +$ cvs co regfiles +U regfiles/sfile +$ +@end example + +@node Ampersand modules +@appendixsubsec Ampersand modules +@cindex Ampersand modules +@cindex &, in modules file + +A module definition can refer to other modules by +including @samp{&@var{module}} in its definition. +@example +@var{mname} [ options ] @var{&module}@dots{} +@end example + +Then getting the module creates a subdirectory for each such +module, in the directory containing the module. For +example, if modules contains + +@example +ampermod &first-dir +@end example + +@noindent +then a checkout will create an @code{ampermod} directory +which contains a directory called @code{first-dir}, +which in turns contains all the directories and files +which live there. For example, the command + +@example +$ cvs co ampermod +@end example + +@noindent +will create the following files: + +@example +ampermod/first-dir/file1 +ampermod/first-dir/file2 +ampermod/first-dir/sdir/sfile +@end example + +There is one quirk/bug: the messages that @sc{cvs} +prints omit the @file{ampermod}, and thus do not +correctly display the location to which it is checking +out the files: + +@example +$ cvs co ampermod +cvs checkout: Updating first-dir +U first-dir/file1 +U first-dir/file2 +cvs checkout: Updating first-dir/sdir +U first-dir/sdir/sfile +$ +@end example + +Do not rely on this buggy behavior; it may get fixed in +a future release of @sc{cvs}. + +@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. + +@node Excluding directories +@appendixsubsec Excluding directories +@cindex Excluding directories, in modules file +@cindex !, in modules file + +An alias module may exclude particular directories from +other modules by using an exclamation mark (@samp{!}) +before the name of each directory to be excluded. + +For example, if the modules file contains: + +@example +exmodule -a !first-dir/sdir first-dir +@end example + +@noindent +then checking out the module @samp{exmodule} will check +out everything in @samp{first-dir} except any files in +the subdirectory @samp{first-dir/sdir}. +@c Note that the "!first-dir/sdir" sometimes must be listed +@c before "first-dir". That seems like a probable bug, in which +@c case perhaps it should be fixed (to allow either +@c order) rather than documented. See modules4 in testsuite. + +@node Module options +@appendixsubsec Module options +@cindex Options, in modules file + +Either regular modules or ampersand modules can contain +options, which supply additional information concerning +the module. + +@table @code +@cindex -d, in modules file +@item -d @var{name} +Name the working directory something other than the +module name. +@c FIXME: Needs a bunch of examples, analogous to the +@c examples for alias, regular, and ampersand modules +@c which show where the files go without -d. + +@cindex Export program +@cindex -e, in modules file +@item -e @var{prog} +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 Checkout program +@cindex -o, in modules file +@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. See @ref{Module program options} for +information on how @var{prog} is called. +@c FIXME: Is it run on server? client? + +@cindex Status of a module +@cindex Module status +@cindex -s, in modules file +@item -s @var{status} +Assign a status to the module. When the module file is +printed with @samp{cvs checkout -s} the modules are +sorted according to primarily module status, and +secondarily according to the module name. This option +has no other meaning. You can use this option for +several things besides status: for instance, list the +person that is responsible for this module. + +@cindex Tag program +@cindex -t, in modules file +@item -t @var{prog} +Specify a program @var{prog} to run whenever files in a +module are tagged with @code{rtag}. @var{prog} runs +with two arguments: the module name and the symbolic +tag specified to @code{rtag}. It is not run +when @code{tag} is executed. Generally you will find +that the @file{taginfo} file is a better solution (@pxref{taginfo}). +@c FIXME: Is it run on server? client? +@c Problems with -t include: +@c * It is run after the tag not before +@c * It doesn't get passed all the information that +@c taginfo does ("mov", &c). +@c * It only is run for rtag, not tag. +@end table + +You should also see @pxref{Module program options} about how the +``program options'' programs are run. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +@node Module program options +@appendixsubsec How the modules file ``program options'' programs are run +@cindex Modules file program options +@cindex -t, in modules file +@cindex -o, in modules file +@cindex -e, in modules file + +@noindent +For checkout, rtag, and export, the program is server-based, and as such the +following applies:- + +If using remote access methods (pserver, ext, etc.), +@sc{cvs} will execute this program on the server from a temporary +directory. The path is searched for this program. + +If using ``local access'' (on a local or remote NFS file system, i.e. +repository set just to a path), +the program will be executed from the newly checked-out tree, if +found there, or alternatively searched for in the path if not. + +The programs are all run after the operation has effectively +completed. + + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Wrappers +@appendixsec The cvswrappers file +@cindex cvswrappers (admin file) +@cindex CVSWRAPPERS, environment variable +@cindex Wrappers + +@c FIXME: need some better way of separating this out +@c by functionality. -m is +@c one feature, and -k is a another. And this discussion +@c should be better motivated (e.g. start with the +@c problems, then explain how the feature solves it). + +Wrappers refers to a @sc{cvs} feature which lets you +control certain settings based on the name of the file +which is being operated on. The settings are @samp{-k} +for binary files, and @samp{-m} for nonmergeable text +files. + +The @samp{-m} option +specifies the merge methodology that should be used when +a non-binary file is updated. @code{MERGE} means the usual +@sc{cvs} behavior: try to merge the files. @code{COPY} +means that @code{cvs update} will refuse to merge +files, as it also does for files specified as binary +with @samp{-kb} (but if the file is specified as +binary, there is no need to specify @samp{-m 'COPY'}). +@sc{cvs} will provide the user with the +two versions of the files, and require the user using +mechanisms outside @sc{cvs}, to insert any necessary +changes. + +@strong{WARNING: do not use @code{COPY} with +@sc{cvs} 1.9 or earlier - such versions of @sc{cvs} will +copy one version of your file over the other, wiping +out the previous contents.} +@c Ordinarily we don't document the behavior of old +@c versions. But this one is so dangerous, I think we +@c must. I almost renamed it to -m 'NOMERGE' so we +@c could say "never use -m 'COPY'". +The @samp{-m} wrapper option only affects behavior when +merging is done on update; it does not affect how files +are stored. See @ref{Binary files}, for more on +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 +-m update methodology value: MERGE or COPY +-k keyword expansion value: expansion mode + +and value is a single-quote delimited value. +@end example + +@ignore +@example +*.nib -f 'unwrap %s' -t 'wrap %s %s' -m 'COPY' +*.c -t 'indent %s %s' +@end example +@c When does the filter need to be an absolute pathname +@c and when will something like the above work? I +@c suspect it relates to the PATH of the server (which +@c in turn depends on all kinds of stuff, e.g. inetd +@c for pserver). I'm not sure whether/where to discuss +@c this. +@c FIXME: What do the %s's stand for? + +@noindent +The above example of a @file{cvswrappers} file +states that all files/directories that end with a @code{.nib} +should be filtered with the @file{wrap} program before +checking the file into the repository. The file should +be filtered though the @file{unwrap} program when the +file is checked out of the repository. The +@file{cvswrappers} file also states that a @code{COPY} +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 +@code{.c} should be filtered with @file{indent} +before being checked into the repository. Unlike the previous +example, no filtering of the @code{.c} file is done when +it is checked out of the repository. +@noindent +The @code{-t} filter is called with two arguments, +the first is the name of the file/directory to filter +and the second is the pathname to where the resulting +filtered file should be placed. + +@noindent +The @code{-f} filter is called with one argument, +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 @sc{cvs}'s operation: +determining when files are modified. @sc{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 @sc{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. +@end ignore + +@c FIXME: We don't document -W or point to where it is +@c documented. Or .cvswrappers. +For 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 +@cindex Commits, administrative support files +@cindex commit files, see Info files + +The @samp{-i} flag in the @file{modules} file can be +used to run a certain program whenever files are +committed (@pxref{modules}). The files described in +this section provide other, more flexible, ways to run +programs whenever something is committed. + +There are three kinds of programs that can be run on +commit. They are specified in files in the repository, +as described below. The following table summarizes the +file names and the purpose of the corresponding +programs. + +@table @file +@item commitinfo +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 loginfo +The specified program is called when the commit is +complete. It receives the log message and some +additional information and can store the log message in +a file, or mail it to appropriate persons, or maybe +post it to a local newsgroup, or@dots{} Your +imagination is the limit! +@end table + +@menu +* syntax:: The common syntax +* commitinfo:: Pre-commit checking +* verifymsg:: How are log messages evaluated? +* loginfo:: Where should log messages be sent? + +* Updating Commit Files:: Updating legacy repositories to stop using + deprecated command line template formats +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node syntax +@appendixsubsec The common syntax +@cindex Info files (syntax) +@cindex Syntax of info files +@cindex Common syntax of info files + +@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 +@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. +@c Also see comment in configure.in about what happens to the +@c syntax if we pick up a system-supplied regexp matcher. + +@item +A whitespace separator---one or more spaces and/or tabs. + +@item +A file name or command-line template. +@end itemize + +@noindent +Blank lines are ignored. Lines that start with the +character @samp{#} are treated as comments. Long lines +unfortunately can @emph{not} be broken in two parts in +any way. + +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. + +@cindex format strings +@cindex format strings, common syntax +@cindex Info files, common syntax, format strings +@cindex Common syntax of info files, format strings +@noindent +@emph{Note: The following information on format strings is valid +as long as the line @code{UseNewInfoFmtStrings=yes} appears in +your repository's config file (@pxref{config}). Otherwise, +default format strings may be appended to the command line and +the @samp{loginfo} file, especially, can exhibit slightly +different behavior. For more information, +@xref{Updating Commit Files}.} + +In the cases where the second segment of the matched line is a +command line template (e.g. @file{commitinfo}, @file{loginfo}, +& @file{verifymsg}), the command line template may contain format +strings which will be replaced with specific values before the +script is run. +@c FIXCVS then FIXME - it really would make sense to allow %r & maybe even %p +@c to be used in rcsinfo to construct a path, but I haven't +@c coded this yet. + +Format strings can represent a single variable or one or more +attributes of a list variable. An example of a list variable +would be the list available to scripts hung on the loginfo hooks +- the list of files which were just committed. In the case of +loginfo, three attributes are available for each list item: file +name, precommit version, and postcommit version. + +Format strings consist of a @samp{%} character followed by an optional +@samp{@{} (required in the multiple list attribute case), a +single format character representing a variable or a single attribute of +list elements or multiple format characters representing attributes of +list elements, and a closing @samp{@}} when the open bracket was present. + +@emph{Flat} format strings (single format characters which get replaced +with a single value) will generate a single argument +to the called script, regardless of whether the replacement variable contains +white space or other special characters. + +List attributes will generate an argument for each attribute +requested for each list item. For example, @samp{%@{sVv@}} +in a @file{loginfo} command template will generate three +arguments (file name, precommit version, postcommit version, +...) for each file committed. As in the flat format string +case, each attribute will be passed in as a single argument +regardless of whether it contains white space or other +special characters. + +@samp{%%} will be replaced with a literal @samp{%}. + +The format strings available to all script hooks are: + +@table @t +@item n +the null, or empty, string +@item p +the name of the directory being operated on within the repository +@item r +the name of the repository (the path portion of @code{$CVSROOT}) +@end table + +Other format strings are file specific. See the docs on the +particular administration files for more information +(@pxref{Administrative files}). + +As an example, the following line in a @file{loginfo} file would +match only the directory @file{module} and any subdirectories of +@file{module}: + +@example +^module\(/\|$\) (echo; echo %p; echo %@{sVv@}; cat) >>$CVSROOT/CVSROOT/commitlog +@end example + +Using this same line and assuming a commit of new revisions +1.5.4.4 and 1.27.4.1 based on old revisions 1.5.4.3 and 1.27, +respectively, of file1 and file2 in module, something like the +following log message should be appended to commitlog: + +@example + +module +file1 1.5.4.3 1.5.4.4 file2 1.27 1.27.4.1 +Update of /cvsroot/module +In directory localhost.localdomain:/home/jrandom/work/module + +Modified Files: + file1 file2 +Log Message: +A log message. +@end example +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node Updating Commit Files +@appendixsubsec Updating legacy repositories to stop using deprecated command line template formats +@cindex Info files (syntax), updating legacy repositories +@cindex Syntax of info files, updating legacy repositories +@cindex Common syntax of info files, updating legacy repositories +New repositories are created set to use the new format strings by default, so +if you are creating a new repository, you shouldn't have to worry about this +section. + +If you are attempting to maintain a legacy repository which was +making use of the @file{commitinfo}, @file{editinfo}, @file{verifymsg}, +@file{loginfo}, and/or @file{taginfo} script hooks, you should have no +immediate problems with using the current @sc{cvs} executable, but your users +will probably start to see deprecation warnings. + +The reason for this is that all of the script hooks have been updated to +use a new command line parser that extensibly supports multiple +@file{loginfo} & @file{notify} style format strings (@pxref{syntax}) +and this support is not completely compatible with the old style format +strings. + +The quick upgrade method is to stick a @samp{1} after each format string +in your old @file{loginfo} file. For example: + +@example +DEFAULT (echo ""; id; echo %@{sVv@}; date; cat) >> $CVSROOT/CVSROOT/commitlog +@end example + +would become: + +@example +DEFAULT (echo ""; id; echo %1@{sVv@}; date; cat) >> $CVSROOT/CVSROOT/commitlog +@end example + +If you were counting on the fact that only the first @samp{%} in the line was +replaced as a format string, you may also have to double up any further +percent signs on the line. + +If you did this all at once and checked it in, everything should still be +running properly. + +Now add the following line to your config file (@pxref{config}): +@example +UseNewInfoFmtStrings=yes +@end example + +Everything should still be running properly, but your users will probably +start seeing new deprecation warnings. + +Dealing with the deprecation warnings now generated by @file{commitinfo}, +@file{editinfo}, @file{verifymsg}, and @file{taginfo} should be easy. Simply +specify what are currently implicit arguments explicitly. This means appending +the following strings to each active command line template in each file: +@table @code +@item commitinfo +@samp{ %r/%p %s} +@item editinfo +@samp{ %l} +@item taginfo +@samp{ %t %o %p %@{sv@}} +@item verifymsg +@samp{ %l} +@end table + +If you don't desire that any of the newly available information be passed to +the scripts hanging off of these hooks, no further modifications to these +files should be necessary to insure current and future compatibility with +@sc{cvs}'s format strings. + +Fixing @file{loginfo} could be a little tougher. The old style +@file{loginfo} format strings caused a single space and comma separated +argument to be passed in in place of the format string. This is what will +continue to be generated due to the deprecated @samp{1} you inserted into +the format strings. + +Since the new format separates each individual item and passes it into the +script as a separate argument (for a good reason - arguments containing commas +and/or white space are now parsable), to remove the deprecated @samp{1} from +your @file{loginfo} command line templates, you will most likely have to +rewrite any scripts called by the hook to handle the new argument format. + +Also note that the way @samp{%} followed by unrecognized characters and by +@samp{@{@}} was treated in past versions of CVS is not strictly adhered to as +there were bugs in the old versions. Specifically, @samp{%@{@}} would eat the +next character and unrecognized strings resolved only to the empty string, +which was counter to what was stated in the documentation. This version will +do what the documentation said it should have (if you were using only some +combination of @samp{%@{sVv@}}, e.g. @samp{%@{sVv@}}, @samp{%@{sV@}}, or +@samp{%v}, you should have no troubles). + +On the bright side, you should have plenty of time to do this before all +support for the old format strings is removed from @sc{cvs}, so you can just +put up with the deprecation warnings for awhile if you like. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node commitinfo +@appendixsubsec Commitinfo +@cindex @file{commitinfo} +@cindex Commits, precommit verification of +@cindex commitinfo (admin file) +@cindex Info files, commitinfo +@cindex Info files, precommit verification of commits + +The @file{commitinfo} file defines programs to execute +whenever @samp{cvs commit} is about to execute. These +programs are used for pre-commit checking to verify +that the modified, added and removed files are really +ready to be committed. This could be used, for +instance, to verify that the changed files conform to +to your site's standards for coding practice. + +As mentioned earlier, each line in the +@file{commitinfo} file consists of a regular expression +and a command-line template. The template can include +a program name and any number of arguments you wish to +supply to it, as well as format strings. For more info, +@pxref{syntax}. + +@cindex format strings, commitinfo admin file +In addition to the common format strings (@pxref{syntax}), +@file{commitinfo} supports: + +@table @t +@item @{s@} +a list of the names of files to be committed +@end table + +@cindex commitinfo (admin file), updating legacy repositories +@cindex compatibility notes, commitinfo admin file +Currently, if no format strings are specified, a default +string of @samp{ %r/%p %@{s@}} will be appended to the command +line template before replacement is performed, but this +feature is deprecated. It is simply in place so that legacy +repositories will remain compatible with the new @sc{cvs} application. +For information on updating, @pxref{Updating Commit Files}. + +@cindex Exit status, of commitinfo +@cindex commitinfo (admin file), exit status +The first line with a regular expression matching the +directory within the repository will be used. If the +command returns a non-zero exit status the commit will +be aborted. +@c FIXME: need example(s) of what "directory within the +@c repository" means. + +@cindex DEFAULT in commitinfo +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. + +@cindex ALL in commitinfo +All occurrences of the name @samp{ALL} appearing as a +regular expression are used in addition to the first +matching regular expression or the name @samp{DEFAULT}. + +@cindex @file{commitinfo}, working directory +@cindex @file{commitinfo}, command environment +The command will be run in the root of the workspace +containing the new versions of any files the user would like +to modify (commit), @emph{or in a copy of the workspace on +the server (@pxref{Remote repositories})}. If a file is +being removed, there will be no copy of the file under the +current directory. If a file is being added, there will be +no corresponding archive file in the repository unless the +file is being resurrected. + +Note that both the repository directory and the corresponding +Attic (@pxref{Attic}) directory may need to be checked to +locate the archive file corresponding to any given file being +committed. Much of the information about the specific commit +request being made, including the destination branch, commit +message, and command line options specified, is not available +to the command. + +@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 +@appendixsubsec Verifying log messages +@cindex @file{verifymsg} (admin file) +@cindex Log message, verifying +@cindex logging, commits + +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. + +@cindex format strings, verifymsg admin file +In addition to the common format strings (@pxref{syntax}), +@file{verifymsg} supports: + +@table @t +@item l +the full path to the file containing the log message to be verified +@end table + +@cindex verifymsg (admin/commit file), updating legacy repositories +@cindex compatibility notes, verifymsg admin file +Currently, if no format strings are specified, a default +string of @samp{ %l} will be appended to the command +line template before replacement is performed, but this +feature is deprecated. It is simply in place so that legacy +repositories will remain compatible with the new @sc{cvs} application. +For information on updating, @pxref{Updating Commit Files}. + +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 +directory, and then overriding it in a subdirectory. + +@cindex DEFAULT in @file{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. + +@cindex Exit status, of @file{verifymsg} +If the verification script exits with a non-zero exit status, +the commit is aborted. + +@cindex @file{verifymsg}, changing the log message +In the default configuration, CVS allows the +verification script to change the log message. This is +controlled via the RereadLogAfterVerify CVSROOT/config +option. + +When @samp{RereadLogAfterVerify=always} or +@samp{RereadLogAfterVerify=stat}, the log message will +either always be reread after the verification script +is run or reread only if the log message file status +has changed. + +@xref{config}, for more on CVSROOT/config options. + +It is NOT a good idea for a @file{verifymsg} script to +interact directly with the user in the various +client/server methods. For the @code{pserver} method, +there is no protocol support for communicating between +@file{verifymsg} and the client on the remote end. For the +@code{ext} and @code{server} methods, it is possible +for CVS to become confused by the characters going +along the same channel as the CVS protocol +messages. See @ref{Remote repositories}, for more +information on client/server setups. In addition, at the time +the @file{verifymsg} script runs, the CVS +server has locks in place in the repository. If control is +returned to the user here then other users may be stuck waiting +for access to the repository. + +This option can be useful if you find yourself using an +rcstemplate that needs to be modified to remove empty +elements or to fill in default values. It can also be +useful if the rcstemplate has changed in the repository +and the CVS/Template was not updated, but is able to be +adapted to the new format by the verification script +that is run by @file{verifymsg}. + +An example of an update might be to change all +occurrences of 'BugId:' to be 'DefectId:' (which can be +useful if the rcstemplate has recently been changed and +there are still checked-out user trees with cached +copies in the CVS/Template file of the older version). + +Another example of an update might be to delete a line +that contains 'BugID: none' from the log message after +validation of that value as being allowed is made. + +@menu +* verifymsg example:: Verifymsg example +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node verifymsg example +@appendixsubsubsec Verifying log messages +@cindex verifymsg, example +The following is a little silly example of a +@file{verifymsg} file, together with the corresponding +@file{rcsinfo} file, the log message template and a +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 sed 1q < $1 | grep '^BugId:[ ]*[0-9][0-9]*$' > /dev/null; then + exit 0 +elif sed 1q < $1 | grep '^BugId:[ ]*none$' > /dev/null; then + # It is okay to allow commits with 'BugId: none', + # but do not put that text into the real log message. + grep -v '^BugId:[ ]*none$' > $1.rewrite + mv $1.rewrite $1 + exit 0 +else + echo "No BugId found." + exit 1 +fi +@end example + +The @file{verifymsg} file contains this line: + +@example +^tc /usr/cvssupport/bugid.verify %l +@end example + +The @file{rcsinfo} file contains this line: + +@example +^tc /usr/cvssupport/tc.template +@end example + +The @file{config} file contains this line: + +@example +RereadLogAfterVerify=always +@end example + + + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node loginfo +@appendixsubsec Loginfo +@cindex loginfo (admin file) +@cindex logging, commits +@cindex Storing log messages +@cindex Mailing log messages +@cindex Distributing log messages +@cindex Log messages + +@c "cvs commit" is not quite right. What we +@c mean is "when the repository gets changed" which +@c also includes "cvs import" and "cvs add" on a directory. +The @file{loginfo} file is used to control where +@samp{cvs commit} log information is sent. @xref{taginfo}, for +how to log tagging information. + +The first entry on a line is a regular expression which is tested +against the directory that the change is being made to, +relative to the @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. +Note that the filter program @strong{must} read @strong{all} +of the log information or @sc{cvs} may fail with a broken pipe signal. + +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. + +All occurrences of the name @samp{ALL} appearing as a +regular expression are used in addition to the first +matching regular expression or @samp{DEFAULT}. + +The first matching regular expression is used. + +@cindex format strings, loginfo admin file +In addition to the common format strings (@pxref{syntax}), +@file{loginfo} supports: + +@table @t +@item @{sVv@} +File attributes, where: +@table @t +@item s +file name +@item V +old version number (pre-checkin) +@item v +new version number (post-checkin) +@end table +@end table + +For example, some valid format strings are @samp{%%}, +@samp{%s}, @samp{%@{s@}}, and @samp{%@{sVv@}}. + +@cindex loginfo (admin file), updating legacy repositories +@cindex compatibility notes, loginfo admin file +Currently, if @samp{UseNewInfoFmtStrings} is not set in the @file{config} +administration file (@pxref{config}), the format strings will be substituted +as they were in past versions of @sc{cvs}, but this feature is deprecated. +It is simply in place so that legacy repositories will remain compatible with +the new @sc{cvs} application. For information on updating, +please see @ref{Updating Commit Files}. + +@xref{commit files}, for a description of the syntax of the @file{loginfo} +file. + +As an example, if @samp{/u/src/master/yoyodyne/tc} is the repository, @samp{%p} +and @samp{%@{sVv@}} are the format strings, and three files (@t{ChangeLog}, +@t{Makefile}, @t{foo.c}) were modified, the output might be: + +@example +yoyodyne/tc ChangeLog 1.1 1.2 Makefile 1.3 1.4 foo.c 1.12 1.13 +@end example + +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 +repositories}). + +@menu +* loginfo example:: Loginfo example +* Keeping a checked out copy:: Updating a tree on every checkin +@end menu + +@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +@node loginfo example +@appendixsubsubsec Loginfo example + +The following @file{loginfo} file, together with the +tiny shell-script below, appends all log messages +to the file @file{$CVSROOT/CVSROOT/commitlog}, +and any commits to the administrative files (inside +the @file{CVSROOT} directory) are also logged in +@file{/usr/adm/cvsroot-log}. +Commits to the @file{prog1} directory are mailed to @t{ceder}. + +@c FIXME: is it a CVS feature or bug that only the +@c first matching line is used? It is documented +@c above, but is it useful? For example, if we wanted +@c to run both "cvs-log" and "Mail" for the CVSROOT +@c directory, it is kind of awkward if +@c only the first matching line is used. +@example +ALL /usr/local/bin/cvs-log $CVSROOT/CVSROOT/commitlog $USER +^CVSROOT\(/\|$\) /usr/local/bin/cvs-log /usr/adm/cvsroot-log $USER +^prog1\(/\|$\) Mail -s "%p %s" ceder +@end example + +The shell-script @file{/usr/local/bin/cvs-log} looks +like this: + +@example +#!/bin/sh +(echo "------------------------------------------------------"; + echo -n "$2 "; + date; + echo; + cat) >> $1 +@end example + +@node Keeping a checked out copy +@appendixsubsubsec 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 for unix (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 directory @code{cyclic-pages} +and its subdirectories 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 (admin file) +@cindex Form for log message +@cindex Log message template +@cindex Template for log message +@cindex logging, commits + +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{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 +a file containing the log message template. + +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. + +All occurrences 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/cvssupport +@c stuff is to use checkoutlist (with xref to the +@c checkoutlist doc). +@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{verifymsg}, for an example @file{rcsinfo} +file. + +When @sc{cvs} is accessing a remote repository, +the contents of @file{rcsinfo} at the time a directory +is first checked out will specify a template. This +template will be updated on all @samp{cvs update} +commands. It will also be added to new directories +added with a @samp{cvs add new-directory} command. +In versions of @sc{cvs} prior to version 1.12, the +@file{CVS/Template} file was not updated. If the +@sc{cvs} server is at version 1.12 or higher an older +client may be used and the @file{CVS/Template} will +be updated from the server. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node taginfo +@appendixsec Taginfo +@cindex taginfo (admin file) +@cindex Tags, logging +@cindex Tags, verifying +The @file{taginfo} file defines programs to execute +when someone executes a @code{tag} or @code{rtag} +command. The @file{taginfo} file has the standard form +for administrative files (@pxref{Administrative +files}), where each line is a regular expression +followed by a command to execute. + +@cindex format strings, taginfo admin file +In addition to the common format strings (@pxref{syntax}), +@file{taginfo} supports: + +@table @t +@item b +tag type (@code{T} for branch, @code{N} for not-branch, or @code{?} for +unknown, as during delete operations) +@item o +operation (@code{add} for @code{tag}, @code{mov} for @code{tag -F}, or +@code{del} for @code{tag -d}) +@item t +tag name +@item @{sVv@} +file attributes, where: +@table @t +@item s +file name +@item V +old version number (for a move or delete operation) +@item v +new version number (for an add or move operation) +@end table +@end table + +For example, some valid format strings are @samp{%%}, @samp{%p}, @samp{%t}, +@samp{%s}, @samp{%@{s@}}, and @samp{%@{sVv@}}. + +@cindex taginfo (admin file), updating legacy repositories +@cindex compatibility notes, taginfo admin file +Currently, if no format strings are specified, a default +string of @samp{ %t %o %p %@{sv@}} will be appended to the command +line template before replacement is performed, but this +feature is deprecated. It is simply in place so that legacy +repositories will remain compatible with the new @sc{cvs} application. +For information on updating, @pxref{Updating Commit Files}. + +@cindex Exit status, of taginfo admin file +@cindex taginfo (admin file), exit status +A non-zero exit of the filter program will cause the tag to be +aborted. + +Here is an example of using @file{taginfo} to log @code{tag} and @code{rtag} +commands. In the @file{taginfo} file put: + +@example +ALL /usr/local/cvsroot/CVSROOT/loggit %t %b %o %p %@{sVv@} +@end example + +@noindent +Where @file{/usr/local/cvsroot/CVSROOT/loggit} contains the +following script: + +@example +#!/bin/sh +echo "$@@" >>/home/kingdon/cvsroot/CVSROOT/taglog +@end example +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node cvsignore +@appendixsec Ignoring files via cvsignore +@cindex cvsignore (admin file), global +@cindex Global cvsignore +@cindex Ignoring files +@c -- This chapter should maybe be moved to the +@c tutorial part of the manual? + +There are certain file names that frequently occur +inside your working copy, but that you don't want to +put under @sc{cvs} control. Examples are all the object +files that you get while you compile your sources. +Normally, when you run @samp{cvs update}, it prints a +line for each file it encounters that it doesn't know +about (@pxref{update output}). + +@sc{cvs} has a list of files (or sh(1) file name patterns) +that it should ignore while running @code{update}, +@code{import} and @code{release}. +@c -- Are those the only three commands affected? +This list is constructed in the following way. + +@itemize @bullet +@item +The list is initialized to include certain file name +patterns: names associated with @sc{cvs} +administration, or with other common source control +systems; common names for patch files, object files, +archive files, and editor backup files; and other names +that are usually artifacts of assorted utilities. +Currently, the default list of ignored file name +patterns is: + +@cindex Ignored files +@cindex Automatically ignored files +@example + RCS SCCS CVS CVS.adm + RCSLOG cvslog.* + tags TAGS + .make.state .nse_depinfo + *~ #* .#* ,* _$* *$ + *.old *.bak *.BAK *.orig *.rej .del-* + *.a *.olb *.o *.obj *.so *.exe + *.Z *.elc *.ln + core +@end example + +@item +The per-repository list in +@file{$CVSROOT/CVSROOT/cvsignore} is appended to +the list, if that file exists. + +@item +The per-user list in @file{.cvsignore} in your home +directory is appended to the list, if it exists. + +@item +Any entries in the environment variable +@code{$CVSIGNORE} is appended to the list. + +@item +Any @samp{-I} options given to @sc{cvs} is appended. + +@item +As @sc{cvs} traverses through your directories, the contents +of any @file{.cvsignore} will be appended to the list. +The patterns found in @file{.cvsignore} are only valid +for the directory that contains them, not for +any sub-directories. +@end itemize + +In any of the 5 places listed above, a single +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. + +Note that the syntax of the ignore files consists of a +series of lines, each of which contains a space +separated list of filenames. This offers no clean way +to specify filenames which contain spaces, but you can +use a workaround like @file{foo?bar} to match a file +named @file{foo bar} (it also matches @file{fooxbar} +and the like). Also note that there is currently no +way to specify comments. +@c FIXCVS? I don't _like_ this syntax at all, but +@c changing it raises all the usual compatibility +@c issues and I'm also not sure what to change it to. + +@node checkoutlist +@appendixsec The checkoutlist file +@cindex checkoutlist + +It may be helpful to use @sc{cvs} to maintain your own +files in the @file{CVSROOT} directory. For example, +suppose that you have a script @file{logcommit.pl} +which you run by including the following line in the +@file{commitinfo} administrative file: + +@example +ALL $CVSROOT/CVSROOT/logcommit.pl %r/%p %s +@end example + +To maintain @file{logcommit.pl} with @sc{cvs} you would +add the following line to the @file{checkoutlist} +administrative file: + +@example +logcommit.pl +@end example + +The format of @file{checkoutlist} is one line for each +file that you want to maintain using @sc{cvs}, giving +the name of the file, followed optionally by more whitespace +and any error message that should print if the file cannot be +checked out into CVSROOT after a commit: + +@example +logcommit.pl Could not update CVSROOT/logcommit.pl. +@end example + +After setting up @file{checkoutlist} in this fashion, +the files listed there will function just like +@sc{cvs}'s built-in administrative files. For example, +when checking in one of the files you should get a +message such as: + +@example +cvs commit: Rebuilding administrative file database +@end example + +@noindent +and the checked out copy in the @file{CVSROOT} +directory should be updated. + +Note that listing @file{passwd} (@pxref{Password +authentication server}) in @file{checkoutlist} is not +recommended for security reasons. + +For information about keeping a checkout out copy in a +more general context than the one provided by +@file{checkoutlist}, see @ref{Keeping a checked out +copy}. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node history file +@appendixsec The history file +@cindex History file +@cindex Log information, saving + +The file @file{$CVSROOT/CVSROOT/history} is used +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{Creating a repository}). + +The file format of the @file{history} file is +documented only in comments in the @sc{cvs} source +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}. + +@node Variables +@appendixsec Expansions in administrative files +@cindex Internal variables +@cindex Variables + +Sometimes in writing an administrative file, you might +want the file to be able to know various things based +on environment @sc{cvs} is running in. There are +several mechanisms to do that. + +To find the home directory of the user running @sc{cvs} +(from the @code{HOME} environment variable), use +@samp{~} followed by @samp{/} or the end of the line. +Likewise for the home directory of @var{user}, use +@samp{~@var{user}}. These variables are expanded on +the server machine, and don't get any reasonable +expansion if pserver (@pxref{Password authenticated}) +is in use; therefore user variables (see below) may be +a better choice to customize behavior based on the user +running @sc{cvs}. +@c Based on these limitations, should we deprecate ~? +@c What is it good for? Are people using it? + +One may want to know about various pieces of +information internal to @sc{cvs}. A @sc{cvs} internal +variable has the syntax @code{$@{@var{variable}@}}, +where @var{variable} starts with a letter and consists +of alphanumeric characters and @samp{_}. If the +character following @var{variable} is a +non-alphanumeric character other than @samp{_}, the +@samp{@{} and @samp{@}} can be omitted. The @sc{cvs} +internal variables are: + +@table @code +@item CVSROOT +@cindex CVSROOT, internal variable +This is the absolute path to the current @sc{cvs} root directory. +@xref{Repository}, for a description of the various +ways to specify this, but note that the internal +variable contains just the directory and not any +of the access method information. + +@item RCSBIN +@cindex RCSBIN, internal variable +In @sc{cvs} 1.9.18 and older, this specified the +directory where @sc{cvs} was looking for @sc{rcs} +programs. Because @sc{cvs} no longer runs @sc{rcs} +programs, specifying this internal variable is now an +error. + +@item CVSEDITOR +@cindex CVSEDITOR, internal variable +@itemx EDITOR +@cindex EDITOR, internal variable +@itemx VISUAL +@cindex VISUAL, internal variable +These all expand to the same value, which is the editor +that @sc{cvs} is using. @xref{Global options}, for how +to specify this. + +@item USER +@cindex USER, internal variable +Username of the user running @sc{cvs} (on the @sc{cvs} +server machine). +When using pserver, this is the user specified in the repository +specification which need not be the same as the username the +server is running as (@pxref{Password authentication server}). +Do not confuse this with the environment variable of the same name. +@end table + +If you want to pass a value to the administrative files +which the user who is running @sc{cvs} can specify, +use a user variable. +@cindex User variables +To expand a user variable, the +administrative file contains +@code{$@{=@var{variable}@}}. To set a user variable, +specify the global option @samp{-s} to @sc{cvs}, with +argument @code{@var{variable}=@var{value}}. It may be +particularly useful to specify this option via +@file{.cvsrc} (@pxref{~/.cvsrc}). + +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 + +@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}. + +All other strings containing @samp{$} are reserved; +there is no way to quote a @samp{$} character so that +@samp{$} represents itself. + +Environment variables passed to administrative files are: + +@table @code +@cindex environment variables, passed to administrative files + +@item CVS_USER +@cindex CVS_USER, environment variable +The @sc{cvs}-specific username provided by the user, if it +can be provided (currently just for the pserver access +method), and to the empty string otherwise. (@code{CVS_USER} +and @code{USER} may differ when @file{$CVSROOT/CVSROOT/passwd} +is used to map @sc{cvs} usernames to system usernames.) + +@item LOGNAME +@cindex LOGNAME, environment variable +The username of the system user. + +@item USER +@cindex USER, environment variable +Same as @code{LOGNAME}. +Do not confuse this with the internal variable of the same name. +@end table + +@node config +@appendixsec The CVSROOT/config configuration file + +@cindex config, in CVSROOT +@cindex CVSROOT/config + +The administrative file @file{config} contains various +miscellaneous settings which affect the behavior of +@sc{cvs}. The syntax is slightly different from the +other administrative files. Variables are not +expanded. Lines which start with @samp{#} are +considered comments. +@c FIXME: where do we define comments for the other +@c administrative files. +Other lines consist of a keyword, @samp{=}, and a +value. Note that this syntax is very strict. +Extraneous spaces or tabs are not permitted. +@c See comments in parseinfo.c:parse_config for more +@c discussion of this strictness. + +Currently defined keywords are: + +@table @code +@cindex RCSBIN, in CVSROOT/config +@item RCSBIN=@var{bindir} +For @sc{cvs} 1.9.12 through 1.9.18, this setting told +@sc{cvs} to look for @sc{rcs} programs in the +@var{bindir} directory. Current versions of @sc{cvs} +do not run @sc{rcs} programs; for compatibility this +setting is accepted, but it does nothing. + +@cindex SystemAuth, in CVSROOT/config +@item SystemAuth=@var{value} +If @var{value} is @samp{yes}, then pserver should check +for users in the system's user database if not found in +@file{CVSROOT/passwd}. If it is @samp{no}, then all +pserver users must exist in @file{CVSROOT/passwd}. +The default is @samp{yes}. For more on pserver, see +@ref{Password authenticated}. + +@ignore +@cindex PreservePermissions, in CVSROOT/config +@item PreservePermissions=@var{value} +Enable support for saving special device files, +symbolic links, file permissions and ownerships in the +repository. The default value is @samp{no}. +@xref{Special Files}, for the full implications of using +this keyword. +@end ignore + +@cindex TopLevelAdmin, in CVSROOT/config +@item TopLevelAdmin=@var{value} +Modify the @samp{checkout} command to create a +@samp{CVS} directory at the top level of the new +working directory, in addition to @samp{CVS} +directories created within checked-out directories. +The default value is @samp{no}. + +This option is useful if you find yourself performing +many commands at the top level of your working +directory, rather than in one of the checked out +subdirectories. The @file{CVS} directory created there +will mean you don't have to specify @code{CVSROOT} for +each command. It also provides a place for the +@file{CVS/Template} file (@pxref{Working directory +storage}). + +@cindex LockDir, in CVSROOT/config +@item LockDir=@var{directory} +Put @sc{cvs} lock files in @var{directory} rather than +directly in the repository. This is useful if you want +to let users read from the repository while giving them +write access only to @var{directory}, not to the +repository. +It can also be used to put the locks on a very fast +in-memory file system to speed up locking and unlocking +the repository. +You need to create @var{directory}, but +@sc{cvs} will create subdirectories of @var{directory} as it +needs them. For information on @sc{cvs} locks, see +@ref{Concurrency}. + +@c Mention this in Compatibility section? +Before enabling the LockDir option, make sure that you +have tracked down and removed any copies of @sc{cvs} 1.9 or +older. Such versions neither support LockDir, nor will +give an error indicating that they don't support it. +The result, if this is allowed to happen, is that some +@sc{cvs} users will put the locks one place, and others will +put them another place, and therefore the repository +could become corrupted. @sc{cvs} 1.10 does not support +LockDir but it will print a warning if run on a +repository with LockDir enabled. + +@cindex LogHistory, in CVSROOT/config +@item LogHistory=@var{value} +Control what is logged to the @file{CVSROOT/history} file (@pxref{history}). +Default of @samp{TOEFWUPCGMAR} (or simply @samp{all}) will log +all transactions. Any subset of the default is +legal. (For example, to only log transactions that modify the +@file{*,v} files, use @samp{LogHistory=TMAR}.) + +@cindex RereadLogAfterVerify, in CVSROOT/config +@cindex @file{verifymsg}, changing the log message +@item RereadLogAfterVerify=@var{value} +Modify the @samp{commit} command such that CVS will reread the +log message after running the program specified by @file{verifymsg}. +@var{value} may be one of @samp{yes} or @samp{always}, indicating that +the log message should always be reread; @samp{no} +or @samp{never}, indicating that it should never be +reread; or @var{value} may be @samp{stat}, indicating +that the file should be checked with the file system +@samp{stat()} function to see if it has changed (see warning below) +before rereading. The default value is @samp{always}. + +@strong{Note: the `stat' mode can cause CVS to pause for up to +one extra second per directory committed. This can be less IO and +CPU intensive but is not recommended for use with large repositories} + +@xref{verifymsg}, for more information on how verifymsg +may be used. + +@cindex UserAdminOptions, in CVSROOT/config +@item UserAdminOptions=@var{value} +Control what options will be allowed with the @code{cvs admin} +command (@pxref{admin}) for users not in the @code{cvsadmin} group. +The @var{value} string is a list of single character options +which should be allowed. If a user who is not a member of the +@code{cvsadmin} group tries to execute any @code{cvs admin} +option which is not listed they will will receive an error message +reporting that the option is restricted. + +If no @code{cvsadmin} group exists on the server, @sc{cvs} will +ignore the @code{UserAdminOptions} keyword (@pxref{admin}). + +When not specified, @code{UserAdminOptions} defaults to +@samp{k}. In other words, it defaults to allowing +users outside of the @code{cvsadmin} group to use the +@code{cvs admin} command only to change the default keyword +expansion mode for files. + +As an example, to restrict users not in the @code{cvsadmin} +group to using @code{cvs admin} to change the default keyword +substitution mode, lock revisions, unlock revisions, and +replace the log message, use @samp{UserAdminOptions=klum}. + +@cindex format strings, config admin file +@cindex config (admin file), updating legacy repositories +@cindex compatibility notes, config admin file +@cindex UseNewInfoFmtStrings, in CVSROOT/config +@item UseNewInfoFmtStrings=@var{value} +Specify whether @sc{cvs} should support the new or old command line +template model for the commit support files (@pxref{commit files}). +This configuration variable began life in deprecation and is only here +in order to give people time to update legacy repositories to use the new +format string syntax before support for the old syntax is removed. For +information on updating your repository to support the new model, +please see @ref{Updating Commit Files}. + +@emph{Note that new repositories (created with the @code{cvs init} command) +will have this value set to @samp{yes}, but the default value is @samp{no}.} +@end table + +@c --------------------------------------------------------------------- +@node Environment variables +@appendix All environment variables which affect CVS +@cindex Environment variables +@cindex Reference manual for variables + +This is a complete list of all environment variables +that affect @sc{cvs}. + +@table @code +@cindex CVSIGNORE, environment variable +@item $CVSIGNORE +A whitespace-separated list of file name patterns that +@sc{cvs} should ignore. @xref{cvsignore}. + +@cindex CVSWRAPPERS, environment variable +@item $CVSWRAPPERS +A whitespace-separated list of file name patterns that +@sc{cvs} should treat as wrappers. @xref{Wrappers}. + +@cindex CVSREAD, environment variable +@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 +read-only. When this is not set, the default behavior +is to permit modification of your working files. + +@cindex CVSREADONLYFS, environment variable +@item $CVSREADONLYFS +Turns on read-only repository mode. This allows one to +check out from a read-only repository, such as within +an anoncvs server, or from a @sc{cd-rom} repository. + +It has the same effect as if the @samp{-R} command-line +option is used. This can also allow the use of +read-only NFS repositories. + +@item $CVSUMASK +Controls permissions of files in the repository. See +@ref{File permissions}. + +@item $CVSROOT +Should contain the full pathname to the root of the @sc{cvs} +source repository (where the @sc{rcs} files are +kept). This information must be available to @sc{cvs} for +most commands to execute; if @code{$CVSROOT} is not set, +or if you wish to override it for one invocation, you +can supply it on the command line: @samp{cvs -d cvsroot +cvs_command@dots{}} Once you have checked out a working +directory, @sc{cvs} stores the appropriate root (in +the file @file{CVS/Root}), so normally you only need to +worry about this when initially checking out a working +directory. + +@item $CVSEDITOR +@cindex CVSEDITOR, environment variable +@itemx $EDITOR +@cindex EDITOR, environment variable +@itemx $VISUAL +@cindex VISUAL, environment variable +Specifies the program to use for recording log messages +during commit. @code{$CVSEDITOR} overrides +@code{$EDITOR}, which overrides @code{$VISUAL}. +See @ref{Committing your changes} for more or +@ref{Global options} for alternative ways of specifying a +log editor. + +@cindex PATH, environment variable +@item $PATH +If @code{$RCSBIN} is not set, and no path is compiled +into @sc{cvs}, it will use @code{$PATH} to try to find all +programs it uses. + +@cindex HOME, environment variable +@item $HOME +@cindex HOMEPATH, environment variable +@item $HOMEPATH +@cindex HOMEDRIVE, environment variable +@item $HOMEDRIVE +Used to locate the directory where the @file{.cvsrc} +file, and other such files, are searched. On Unix, @sc{cvs} +just checks for @code{HOME}. On Windows NT, the system will +set @code{HOMEDRIVE}, for example to @samp{d:} and @code{HOMEPATH}, +for example to @file{\joe}. On Windows 95, you'll +probably need to set @code{HOMEDRIVE} and @code{HOMEPATH} yourself. +@c We are being vague about whether HOME works on +@c Windows; see long comment in windows-NT/filesubr.c. + +@cindex CVS_RSH, environment variable +@item $CVS_RSH +Specifies the external program which @sc{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 +repository using @sc{rsh}. It specifies the name of +the program to start on the server side (and any +necessary arguments) when accessing a remote repository +using the @code{:ext:}, @code{:fork:}, or @code{:server:} access methods. +The default value for @code{:ext:} and @code{:server:} is @code{cvs}; +the default value for @code{:fork:} is the name used to run the client. +@pxref{Connecting via rsh} + +@item $CVS_PASSFILE +Used in client-server mode when accessing the @code{cvs +login server}. Default value is @file{$HOME/.cvspass}. +@pxref{Password authentication client} + +@cindex CVS_CLIENT_PORT +@item $CVS_CLIENT_PORT +Used in client-server mode to set the port to use when accessing the server +via Kerberos, GSSAPI, or @sc{cvs}'s password authentication protocol +if the port is not specified in the CVSROOT. +@pxref{Remote repositories} + +@cindex CVS_PROXY_PORT +@item $CVS_PROXY_PORT +Used in client-server mode to set the port to use when accessing a server +via a web proxy, if the port is not specified in the CVSROOT. Works with +GSSAPI, and the password authentication protocol. +@pxref{Remote repositories} + +@cindex CVS_RCMD_PORT, environment variable +@item $CVS_RCMD_PORT +Used in client-server mode. If set, specifies the port +number to be used when accessing the @sc{rcmd} demon on +the server side. (Currently not used for Unix clients). + +@cindex CVS_CLIENT_LOG, environment variable +@item $CVS_CLIENT_LOG +Used for debugging only in client-server +mode. If set, everything sent to the server is logged +into @file{@code{$CVS_CLIENT_LOG}.in} and everything +sent from the server is logged into +@file{@code{$CVS_CLIENT_LOG}.out}. + +@cindex CVS_SERVER_SLEEP, environment variable +@item $CVS_SERVER_SLEEP +Used only for debugging the server side in +client-server mode. If set, delays the start of the +server child process the specified amount of +seconds so that you can attach to it with a debugger. + +@cindex CVS_IGNORE_REMOTE_ROOT, environment variable +@item $CVS_IGNORE_REMOTE_ROOT +For @sc{cvs} 1.10 and older, setting this variable +prevents @sc{cvs} from overwriting the @file{CVS/Root} +file when the @samp{-d} global option is specified. +Later versions of @sc{cvs} do not rewrite +@file{CVS/Root}, so @code{CVS_IGNORE_REMOTE_ROOT} has no +effect. + +@cindex CVS_LOCAL_BRANCH_NUM, environment variable +@item $CVS_LOCAL_BRANCH_NUM +Setting this variable allows some control over the +branch number that is assigned. This is specifically to +support the local commit feature of CVSup. If one sets +@code{CVS_LOCAL_BRANCH_NUM} to (say) 1000 then branches +the local repository, the revision numbers will look +like 1.66.1000.xx. There is almost a dead-set certainty +that there will be no conflicts with version numbers. + +@cindex COMSPEC, environment variable +@item $COMSPEC +Used under OS/2 only. It specifies the name of the +command interpreter and defaults to @sc{cmd.exe}. + +@cindex TMPDIR, environment variable +@item $TMPDIR +@cindex TMP, environment variable +@itemx $TMP +@cindex TEMP, environment variable +@itemx $TEMP +@cindex Temporary files, location of +@c This is quite nuts. We don't talk about tempnam +@c or mkstemp which we sometimes use. The discussion +@c of "Global options" is semi-incoherent. +@c I'm not even sure those are the only inaccuracies. +@c Furthermore, the conventions are +@c pretty crazy and they should be simplified. +Directory in which temporary files are located. +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). Note that +if your server and client are both running @sc{cvs} +1.9.10 or later, @sc{cvs} will not invoke an external +@code{patch} program. + +@cindex CVS_PID, environment variable +@item $CVS_PID +This is the process identification (aka pid) number of +the @sc{cvs} process. It is often useful in the +programs and/or scripts specified by the +@file{commitinfo}, @file{verifymsg}, @file{loginfo} +files. +@end table + +@node Compatibility +@appendix 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 " 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 Wait, can't CVS just detect the case in which a file +@c is in the Attic but the head revision is not dead? +@c Not sure whether this should produce a warning or +@c something, and probably needs further thought, but +@c it would appear that the situation can be detected. +@c +@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 bug fixes, 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 Troubleshooting +@appendix Troubleshooting + +If you are having trouble with @sc{cvs}, this appendix +may help. If there is a particular error message which +you are seeing, then you can look up the message +alphabetically. If not, you can look through the +section on other problems to see if your problem is +mentioned there. + +@menu +* Error messages:: Partial list of CVS errors +* Connection:: Trouble making a connection to a CVS server +* Other problems:: Problems not readily listed by error message +@end menu + +@ignore +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@c @node Bad administrative files +@appendixsec Bad administrative files + +@c -- Give hints on how to fix them +@end ignore + +@node Error messages +@appendixsec Partial list of error messages + +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. + +The messages are alphabetical, but introductory text +such as @samp{cvs update: } is not considered in +ordering them. + +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). +@c If we want to start retiring messages, perhaps we +@c should pick a cutoff version (for example, no more +@c messages which are specific to versions before 1.9) +@c and then move the old messages to an "old messages" +@c node rather than deleting them completely. + +@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 @var{file}:@var{line}: Assertion '@var{text}' failed +The exact format of this message may vary depending on +your system. It indicates a bug in @sc{cvs}, which can +be handled as described in @ref{BUGS}. + +@item cvs @var{command}: authorization failed: server @var{host} rejected access +This is a generic response when trying to connect to a +pserver server which chooses not to provide a +specific reason for denying authorization. Check that +the username and password specified are correct and +that the @code{CVSROOT} specified is allowed by @samp{--allow-root} +in @file{inetd.conf}. See @ref{Password authenticated}. + +@item cvs @var{command}: conflict: removed @var{file} was modified by second party +This message indicates that you removed a file, and +someone else modified it. To resolve the conflict, +first run @samp{cvs add @var{file}}. If desired, look +at the other party's modification to decide whether you +still want to remove it. If you don't want to remove +it, stop here. If you do want to remove it, proceed +with @samp{cvs remove @var{file}} and commit your +removal. +@c Tests conflicts2-142b* in sanity.sh test for this. + +@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 This has been seen in a variety of tests, including +@c multibranch-2, multibranch-5, and basic1-24-rm-rm, +@c so it doesn't seem particularly specific to any one +@c test. + +@item cvs [server aborted]: Cannot check out files into the repository itself +The obvious cause for this message (especially for +non-client/server @sc{cvs}) is that the @sc{cvs} root +is, for example, @file{/usr/local/cvsroot} and you try +to check out files when you are in a subdirectory, such +as @file{/usr/local/cvsroot/test}. However, there is a +more subtle cause, which is that the temporary +directory on the server is set to a subdirectory of the +root (which is also not allowed). If this is the +problem, set the temporary directory to somewhere else, +for example @file{/var/tmp}; see @code{TMPDIR} in +@ref{Environment variables}, for how to set the +temporary directory. + +@item cannot commit files as 'root' +See @samp{'root' is not allowed to commit files}. + +@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). +@c For another example, "cvs co foo/bar" where foo exists. +@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. + +@c This is more obscure than it might sound; it only +@c happens if you run "cvs init" from a directory which +@c contains a CVS/Root file at the start. +@item cvs [init aborted]: cannot open CVS/Root: No such file or directory +This message is harmless. Provided it is not +accompanied by other errors, the operation has +completed successfully. This message should not occur +with current versions of @sc{cvs}, but it is documented +here for the benefit of @sc{cvs} 1.9 and older. + +@item cvs server: cannot open /root/.cvsignore: Permission denied +@itemx cvs [server aborted]: can't chdir(/root): Permission denied +See @ref{Connection}. + +@item cvs [checkout aborted]: cannot rename file @var{file} to CVS/,,@var{file}: Invalid argument +This message has been reported as intermittently +happening with @sc{cvs} 1.9 on Solaris 2.5. The cause is +unknown; if you know more about what causes it, let us +know as described in @ref{BUGS}. + +@item cvs [@var{command} aborted]: cannot start server via rcmd +This, unfortunately, is a rather nonspecific error +message which @sc{cvs} 1.9 will print if you are +running the @sc{cvs} client and it is having trouble +connecting to the server. Current versions of @sc{cvs} +should print a much more specific error message. If +you get this message when you didn't mean to run the +client at all, you probably forgot to specify +@code{:local:}, as described in @ref{Repository}. + +@item ci: @var{file},v: bad diff output line: Binary files - and /tmp/T2a22651 differ +@sc{cvs} 1.9 and older will print this message +when trying to check in a binary file if +@sc{rcs} is not correctly installed. Re-read the +instructions that came with your @sc{rcs} distribution +and the @sc{install} file in the @sc{cvs} +distribution. Alternately, upgrade to a current +version of @sc{cvs}, which checks in files itself +rather than via @sc{rcs}. + +@item cvs checkout: could not check out @var{file} +With @sc{cvs} 1.9, this can mean that the @code{co} program +(part of @sc{rcs}) returned a failure. It should be +preceded by another error message, however it has been +observed without another error message and the cause is +not well-understood. With the current version of @sc{cvs}, +which does not run @code{co}, if this message occurs +without another error message, it is definitely a @sc{cvs} +bug (@pxref{BUGS}). +@c My current suspicion is that the RCS in the rcs (not +@c cvs/winnt/rcs57nt.zip) directory on the _Practical_ +@c CD is bad (remains to be confirmed). +@c There is also a report of something which looks +@c very similar on SGI, Irix 5.2, so I dunno. + +@item cvs [login aborted]: could not find out home directory +This means that you need to set the environment +variables that @sc{cvs} uses to locate your home directory. +See the discussion of @code{HOME}, @code{HOMEDRIVE}, and @code{HOMEPATH} in +@ref{Environment variables}. + +@item cvs update: could not merge revision @var{rev} of @var{file}: No such file or directory +@sc{cvs} 1.9 and older will print this message if there was +a problem finding the @code{rcsmerge} program. Make +sure that it is in your @code{PATH}, or upgrade to a +current version of @sc{cvs}, which does not require +an external @code{rcsmerge} program. + +@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}. +If both the client and the server are running a current +version of @sc{cvs}, then there is no need for an +external patch program and you should not see this +message. But if either client or server is running +@sc{cvs} 1.9, then you need @code{patch}. + +@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 +There is a known bug in the server for @sc{cvs} 1.9.18 +and older which can cause this. For me, this was +reproducible if I used the @samp{-t} global option. It +was fixed by Andy Piper's 14 Nov 1997 change to +src/filesubr.c, if anyone is curious. +If you see the message, +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}. + +@item cvs [update aborted]: EOF in key in RCS file @var{file},v +@itemx cvs [checkout aborted]: EOF while looking for end of string in RCS file @var{file},v +This means that there is a syntax error in the given +@sc{rcs} file. Note that this might be true even if @sc{rcs} can +read the file OK; @sc{cvs} does more error checking of +errors in the RCS file. That is why you may see this +message when upgrading from @sc{cvs} 1.9 to @sc{cvs} +1.10. The likely cause for the original corruption is +hardware, the operating system, or the like. Of +course, if you find a case in which @sc{cvs} seems to +corrupting the file, by all means report it, +(@pxref{BUGS}). +There are quite a few variations of this error message, +depending on exactly where in the @sc{rcs} file @sc{cvs} +finds the syntax error. + +@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}. + +@c This message comes from "co", and I believe is +@c possible only with older versions of CVS which call +@c co. The problem with being able to create the bogus +@c RCS file still exists, though (and I think maybe +@c there is a different symptom(s) now). +@c FIXME: Would be nice to have a more exact wording +@c for this message. +@item missing author +Typically this can happen if you created an RCS file +with your username set to empty. @sc{cvs} will, bogusly, +create an illegal RCS file with no value for the author +field. The solution is to make sure your username is +set to a non-empty value and re-create the RCS file. +@c "make sure your username is set" is complicated in +@c and of itself, as there are the environment +@c variables the system login name, &c, and it depends +@c on the version of CVS. + +@item cvs [checkout aborted]: no such tag @var{tag} +This message means that @sc{cvs} isn't familiar with +the tag @var{tag}. Usually this means that you have +mistyped a tag name; however there are (relatively +obscure) cases in which @sc{cvs} will require you to +@c Search sanity.sh for "no such tag" to see some of +@c the relatively obscure cases. +try a few other @sc{cvs} commands involving that tag, +before you find one which will cause @sc{cvs} to update +@cindex CVSROOT/val-tags file, forcing tags into +@cindex val-tags file, forcing tags into +the @file{val-tags} file; see discussion of val-tags in +@ref{File permissions}. You only need to worry about +this once for a given tag; when a tag is listed in +@file{val-tags}, it stays there. Note that using +@samp{-f} to not require tag matches does not override +this check; see @ref{Common options}. + +@item *PANIC* administration files missing +This typically means that there is a directory named +@sc{cvs} but it does not contain the administrative files +which @sc{cvs} puts in a CVS directory. If the problem is +that you created a CVS directory via some mechanism +other than @sc{cvs}, then the answer is simple, use a name +other than @sc{cvs}. If not, it indicates a @sc{cvs} bug +(@pxref{BUGS}). + +@item rcs error: Unknown option: -x,v/ +This message will be followed by a usage message for +@sc{rcs}. It means that you have an old version of +@sc{rcs} (probably supplied with your operating +system), as well as an old version of @sc{cvs}. +@sc{cvs} 1.9.18 and earlier only work with @sc{rcs} version 5 and +later; current versions of @sc{cvs} do not run @sc{rcs} programs. +@c For more information on installing @sc{cvs}, see +@c (FIXME: where? it depends on whether you are +@c getting binaries or sources or what). +@c The message can also say "ci error" or something +@c instead of "rcs error", I suspect. + +@item cvs [server aborted]: received broken pipe signal +This message can be caused by a loginfo program that fails to +read all of the log information from its standard input. +If you find it happening in any other circumstances, +please let us know as described in @ref{BUGS}. + +@item 'root' is not allowed to commit files +When committing a permanent change, @sc{cvs} makes 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), +@sc{cvs} cannot determine who is actually making the change. +As such, by default, @sc{cvs} disallows changes to be committed by users +logged in as "root". (You can disable this option by passing the +@code{--enable-rootcommit} option to @file{configure} and recompiling @sc{cvs}. +On some systems this means editing the appropriate @file{config.h} file +before building @sc{cvs}.) + +@item Too many arguments! +This message is typically printed by the @file{log.pl} +script which is in the @file{contrib} directory in the +@sc{cvs} source distribution. In some versions of +@sc{cvs}, @file{log.pl} has been part of the default +@sc{cvs} installation. The @file{log.pl} script gets +called from the @file{loginfo} administrative file. +Check that the arguments passed in @file{loginfo} match +what your version of @file{log.pl} expects. In +particular, the @file{log.pl} from @sc{cvs} 1.3 and +older expects the log file as an argument whereas the +@file{log.pl} from @sc{cvs} 1.5 and newer expects the +log file to be specified with a @samp{-f} option. Of +course, if you don't need @file{log.pl} you can just +comment it out of @file{loginfo}. + +@item cvs [update aborted]: unexpected EOF reading @var{file},v +See @samp{EOF in key in RCS file}. + +@item cvs [login aborted]: unrecognized auth response from @var{server} +This message typically means that the server is not set +up properly. For example, if @file{inetd.conf} points +to a nonexistent cvs executable. To debug it further, +find the log file which inetd writes +(@file{/var/log/messages} or whatever inetd uses on +your system). For details, see @ref{Connection}, and +@ref{Password authentication server}. + +@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}. @sc{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 @var{file}} and you are ready +to @code{cvs commit}. If it detects conflicts it will +print a message saying so, will report @samp{C @var{file}}, +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 +simplest solution is to upgrade to a current version of +@sc{cvs}, which does not rely on external +@code{rcsmerge} or @code{diff3} programs. + +@item warning: unrecognized response `@var{text}' from cvs server +If @var{text} contains a valid response (such as +@samp{ok}) followed by an extra carriage return +character (on many systems this will cause the second +part of the message to overwrite the first part), then +it probably means that you are using the @samp{:ext:} +access method with a version of rsh, such as most +non-unix rsh versions, which does not by default +provide a transparent data stream. In such cases you +probably want to try @samp{:server:} instead of +@samp{:ext:}. If @var{text} is something else, this +may signify a problem with your @sc{cvs} server. +Double-check your installation against the instructions +for setting up the @sc{cvs} server. +@c FIXCVS: should be printing CR as \r or \015 or some +@c such, probably. + +@item cvs commit: [@var{time}] waiting for @var{user}'s lock in @var{directory} +This is a normal message, not an error. See +@ref{Concurrency}, for more details. + +@item cvs commit: warning: editor session failed +@cindex Exit status, of editor +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 +@code{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 + +@node Connection +@appendixsec Trouble making a connection to a CVS server + +This section concerns what to do if you are having +trouble making a connection to a @sc{cvs} server. If +you are running the @sc{cvs} command line client +running on Windows, first upgrade the client to +@sc{cvs} 1.9.12 or later. The error reporting in +earlier versions provided much less information about +what the problem was. If the client is non-Windows, +@sc{cvs} 1.9 should be fine. + +If the error messages are not sufficient to track down +the problem, the next steps depend largely on which +access method you are using. + +@table @code +@cindex :ext:, troubleshooting +@item :ext: +Try running the rsh program from the command line. For +example: "rsh servername cvs -v" should print @sc{cvs} +version information. If this doesn't work, you need to +fix it before you can worry about @sc{cvs} problems. + +@cindex :server:, troubleshooting +@item :server: +You don't need a command line rsh program to use this +access method, but if you have an rsh program around, +it may be useful as a debugging tool. Follow the +directions given for :ext:. + +@cindex :pserver:, troubleshooting +@item :pserver: +Errors along the lines of "connection refused" typically indicate +that inetd isn't even listening for connections on port 2401 +whereas errors like "connection reset by peer", +"received broken pipe signal", "recv() from server: EOF", +or "end of file from server" +typically indicate that inetd is listening for +connections but is unable to start @sc{cvs} (this is frequently +caused by having an incorrect path in @file{inetd.conf} +or by firewall software rejecting the connection). +"unrecognized auth response" errors are caused by a bad command +line in @file{inetd.conf}, typically an invalid option or forgetting +to put the @samp{pserver} command at the end of the line. +Another less common problem is invisible control characters that +your editor "helpfully" added without you noticing. + +One good debugging tool is to "telnet servername +2401". After connecting, send any text (for example +"foo" followed by return). If @sc{cvs} is working +correctly, it will respond with + +@example +cvs [pserver aborted]: bad auth protocol start: foo +@end example + +If instead you get: + +@example +Usage: cvs [cvs-options] command [command-options-and-arguments] +... +@end example + +@noindent +then you're missing the @samp{pserver} command at the end of the +line in @file{inetd.conf}; check to make sure that the entire command +is on one line and that it's complete. + +Likewise, if you get something like: + +@example +Unknown command: `pserved' + +CVS commands are: + add Add a new file/directory to the repository +... +@end example + +@noindent +then you've misspelled @samp{pserver} in some way. If it isn't +obvious, check for invisible control characters (particularly +carriage returns) in @file{inetd.conf}. + +If it fails to work at all, then make sure inetd is working +right. Change the invocation in @file{inetd.conf} to run the +echo program instead of cvs. For example: + +@example +2401 stream tcp nowait root /bin/echo echo hello +@end example + +After making that change and instructing inetd to +re-read its configuration file, "telnet servername +2401" should show you the text hello and then the +server should close the connection. If this doesn't +work, you need to fix it before you can worry about +@sc{cvs} problems. + +On AIX systems, the system will often have its own +program trying to use port 2401. This is AIX's problem +in the sense that port 2401 is registered for use with +@sc{cvs}. I hear that there is an AIX patch available +to address this problem. + +Another good debugging tool is the @samp{-d} +(debugging) option to inetd. Consult your system +documentation for more information. + +If you seem to be connecting but get errors like: + +@example +cvs server: cannot open /root/.cvsignore: Permission denied +cvs [server aborted]: can't chdir(/root): Permission denied +@end example + +@noindent +then you probably haven't specified @samp{-f} in @file{inetd.conf}. +(In releases prior to @sc{cvs} 1.11.1, this problem can be caused by +your system setting the @code{$HOME} environment variable +for programs being run by inetd. In this case, you can either +have inetd run a shell script that unsets @code{$HOME} and then runs +@sc{cvs}, or you can use @code{env} to run @sc{cvs} with a pristine +environment.) + +If you can connect successfully for a while but then can't, +you've probably hit inetd's rate limit. +(If inetd receives too many requests for the same service +in a short period of time, it assumes that something is wrong +and temporarily disables the service.) +Check your inetd documentation to find out how to adjust the +rate limit (some versions of inetd have a single rate limit, +others allow you to set the limit for each service separately.) +@end table + +@node Other problems +@appendixsec Other common problems + +Here is a list of problems which do not fit into the +above categories. They are in no particular order. + +@itemize @bullet +@item +On Windows, if there is a 30 second or so delay when +you run a @sc{cvs} command, it may mean that you have +your home directory set to @file{C:/}, for example (see +@code{HOMEDRIVE} and @code{HOMEPATH} in +@ref{Environment variables}). @sc{cvs} expects the home +directory to not end in a slash, for example @file{C:} +or @file{C:\cvs}. +@c FIXCVS: CVS should at least detect this and print an +@c error, presumably. + +@item +If you are running @sc{cvs} 1.9.18 or older, and +@code{cvs update} finds a conflict and tries to +merge, as described in @ref{Conflicts example}, but +doesn't tell you there were conflicts, then you may +have an old version of @sc{rcs}. The easiest solution +probably is to upgrade to a current version of +@sc{cvs}, which does not rely on external @sc{rcs} +programs. +@end itemize + +@c --------------------------------------------------------------------- +@node Credits +@appendix Credits + +@cindex Contributors (manual) +@cindex Credits (manual) +Roland Pesch, then of Cygnus Support <@t{roland@@wrs.com}> +wrote the manual pages which were distributed with +@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. + +The mailing-list @code{info-cvs} is sometimes +informative. I have included information from postings +made by the following persons: +David G. Grubbs <@t{dgg@@think.com}>. + +Some text has been extracted from the man pages for +@sc{rcs}. + +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}>, +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 +@appendix Dealing with bugs in CVS or this manual + +@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 +If you want someone to help you and fix bugs that you +report, there are companies which will do that for a +fee. One such company is: + +@cindex Ximbiot +@cindex Support, getting CVS support +@example +Ximbiot +319 S. River St. +Harrisburg, PA 17104-1657 +USA +Email: info@@ximbiot.com +Phone: (717) 579-6168 +Fax: (717) 234-3125 +@url{http://ximbiot.com/} + +@end example + +@item +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 +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 +There may be resources on the net which can help. Two +good places to start are: + +@example +@url{http://www.cvshome.org} +@url{http://www.loria.fr/~molli/cvs-index.html} +@end example + +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. + +@item +It is also possible to report bugs to @email{bug-cvs@@gnu.org}. +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 @email{bug-cvs@@gnu.org}. Note +that submissions to @email{bug-cvs@@gnu.org} 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 +@email{bug-cvs@@gnu.org}; those maintainers who want to hear +about such bug reports read @email{bug-cvs@@gnu.org}. Also note +that sending a bug report to other mailing lists or +newsgroups is @emph{not} a substitute for sending it to +@email{bug-cvs@@gnu.org}. 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 @email{bug-cvs@@gnu.org}. +@end itemize + +@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 Index +@unnumbered Index +@cindex Index + +@printindex cp + +@summarycontents + +@contents + +@bye + +Local Variables: +fill-column: 55 +End: diff --git a/contrib/cvs-1.12.9/doc/cvsclient.texi b/contrib/cvs-1.12.9/doc/cvsclient.texi new file mode 100644 index 0000000000..29fe8c0eb8 --- /dev/null +++ b/contrib/cvs-1.12.9/doc/cvsclient.texi @@ -0,0 +1,2111 @@ +\input texinfo @c -*- texinfo -*- + +@setfilename cvsclient.info +@include version-client.texi + +@dircategory Programming +@direntry +* cvsclient: (cvsclient). The CVS client/server protocol. +@end direntry + +@node Top +@top CVS Client/Server + +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{VERSION} of the protocol +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. +* Connection and Authentication:: Various ways to connect to the server +* Password scrambling:: Scrambling used by pserver +* Protocol:: Complete description of the protocol +* Protocol Notes:: Possible enhancements, limitations, etc. of the protocol +@end menu + +@node Introduction +@chapter Introduction + +CVS is a version control system (with some additional configuration +management functionality). It maintains a central @dfn{repository} +which stores files (often source code), including past versions, +information about who modified them and when, and so on. People who +wish to look at or modify those files, known as @dfn{developers}, use +CVS to @dfn{check out} a @dfn{working directory} from the repository, to +@dfn{check in} new versions of files to the repository, and other +operations such as viewing the modification history of a file. If +developers are connected to the repository by a network, particularly a +slow or flaky one, the most efficient way to use the network is with the +CVS-specific protocol described in this document. + +Developers, using the machine on which they store their working +directory, run the CVS @dfn{client} program. To perform operations +which cannot be done locally, it connects to the CVS @dfn{server} +program, which maintains the repository. For more information on how +to connect see @ref{Connection and Authentication}. + +This document describes the CVS protocol. Unfortunately, it does not +yet completely document one aspect of the protocol---the detailed +operation of each CVS command and option---and one must look at the CVS +user documentation, @file{cvs.texinfo}, for that information. The +protocol is non-proprietary (anyone who wants to is encouraged to +implement it) and an implementation, known as CVS, is available under +the GNU Public License. The CVS distribution, containing this +implementation, @file{cvs.texinfo}, and a copy (possibly more or less up +to date than what you are reading now) of this document, +@file{cvsclient.texi}, can be found at the usual GNU FTP sites, with a +filename such as @file{cvs-@var{version}.tar.gz}. + +This is version @value{VERSION} of the protocol specification. This +version number is intended only to aid in distinguishing different +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 @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 +flexible. + +@node Goals +@chapter Goals + +@itemize @bullet +@item +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. 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} and @samp{cvs pserver}). + +@item +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 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. + +@item +It is a general design goal to provide only one way to do a given +operation (where possible). For example, implementations have no choice +about whether to terminate lines with linefeeds or some other +character(s), and request and response names are case-sensitive. This +is to enhance interoperability. If a protocol allows more than one way +to do something, it is all too easy for some implementations to support +only some of them (perhaps accidentally). +@c I vaguely remember reading, probably in an RFC, about the problems +@c that were caused when some people decided that SMTP should accept +@c other line termination (in the message ("DATA")?) than CRLF. However, I +@c can't seem to track down the reference. +@end itemize + +@node Connection and Authentication +@chapter How to Connect to and Authenticate Oneself to the CVS server + +Connection and authentication occurs before the CVS protocol itself is +started. There are several ways to connect. + +@table @asis +@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, +SSH, or any similar mechanism. The client may allow the user to specify +the name of the server program; the default is @code{cvs}. It is +invoked with one argument, @code{server}. Once it invokes the server, +the client proceeds to start the cvs protocol. + +@item kserver +The kerberized server listens on a port (in the current implementation, +by having inetd call "cvs kserver") which defaults to 1999. The client +connects, sends the usual kerberos authentication information, and then +starts the cvs protocol. Note: port 1999 is officially registered for +another use, and in any event one cannot register more than one port for +CVS, so GSS-API (see below) is recommended instead of kserver as a way +to support kerberos. + +@item pserver +The name @dfn{pserver} is somewhat confusing. It refers to both a +generic framework which allows the CVS protocol to support several +authentication mechanisms, and a name for a specific mechanism which +transfers a username and a cleartext password. Servers need not support +all mechanisms, and in fact servers will typically want to support only +those mechanisms which meet the relevant security needs. + +The pserver server listens on a port (in the current +implementation, by having inetd call "cvs pserver") which defaults to +2401 (this port is officially registered). The client +connects, and sends the following: + +@itemize @bullet +@item +the string @samp{BEGIN AUTH REQUEST}, a linefeed, +@item +the cvs root, a linefeed, +@item +the username, a linefeed, +@item +the password trivially encoded (see @ref{Password scrambling}), a +linefeed, +@item +the string @samp{END AUTH REQUEST}, and a linefeed. +@end itemize + +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 possible server responses (each of which is followed by a linefeed) +are the following. Note that although there is a small similarity +between this authentication protocol and the cvs protocol, they are +separate. + +@table @code +@item I LOVE YOU +The authentication is successful. The client proceeds with the cvs +protocol itself. + +@item I HATE YOU +The authentication fails. After sending this response, the server may +close the connection. It is up to the server to decide whether to give +this response, which is generic, or a more specific response using +@samp{E} and/or @samp{error}. + +@item E @var{text} +Provide a message for the user. After this reponse, the authentication +protocol continues with another response. Typically the server will +provide a series of @samp{E} responses followed by @samp{error}. +Compatibility note: @sc{cvs} 1.9.10 and older clients will print +@code{unrecognized auth response} and @var{text}, and then exit, upon +receiving this response. + +@item error @var{code} @var{text} +The authentication fails. After sending this response, the server may +close the connection. The @var{code} is a code describing why it +failed, intended for computer consumption. The only code currently +defined is @samp{0} which is nonspecific, but clients must silently +treat any unrecognized codes as nonspecific. +The @var{text} should be supplied to the +user. Compatibility note: @sc{cvs} 1.9.10 and older clients will print +@code{unrecognized auth response} and @var{text}, and then exit, upon +receiving this response. +Note that @var{text} for this response, or the @var{text} in an @code{E} +response, is not designed for machine parsing. More vigorous use of +@var{code}, or future extensions, will be needed to prove a cleaner +machine-parseable indication of what the error was. +@end table + +@c If you are thinking of putting samp or code around BEGIN AUTH REQUEST +@c and friends, watch for overfull hboxes. +If the client wishes to merely authenticate without starting the cvs +protocol, the procedure is the same, except BEGIN AUTH REQUEST is +replaced with BEGIN VERIFICATION REQUEST, END AUTH REQUEST +is replaced with END VERIFICATION REQUEST, and upon receipt of +I LOVE YOU the connection is closed rather than continuing. + +Another mechanism is GSSAPI authentication. GSSAPI is a +generic interface to security services such as kerberos. GSSAPI is +specified in RFC2078 (GSSAPI version 2) and RFC1508 (GSSAPI version 1); +we are not aware of differences between the two which affect the +protocol in incompatible ways, so we make no attempt to specify one +version or the other. +The procedure here is to start with @samp{BEGIN +GSSAPI REQUEST}. GSSAPI authentication information is then exchanged +between the client and the server. Each packet of information consists +of a two byte big endian length, followed by that many bytes of data. +After the GSSAPI authentication is complete, the server continues with +the responses described above (@samp{I LOVE YOU}, etc.). + +@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 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 Password scrambling +@chapter Password scrambling algorithm + +The pserver authentication protocol, as described in @ref{Connection and +Authentication}, trivially encodes the passwords. This is only to +prevent inadvertent compromise; it provides no protection against even a +relatively unsophisticated attacker. For comparison, HTTP Basic +Authentication (as described in RFC2068) uses BASE64 for a similar +purpose. CVS uses its own algorithm, described here. + +The scrambled password starts with @samp{A}, which serves to identify +the scrambling algorithm in use. After that follows a single octet for +each character in the password, according to a fixed encoding. The +values are shown here, with the encoded values in decimal. Control +characters, space, and characters outside the invariant ISO 646 +character set are not shown; such characters are not recommended for use +in passwords. There is a long discussion of character set issues in +@ref{Protocol Notes}. + +@example + 0 111 P 125 p 58 +! 120 1 52 A 57 Q 55 a 121 q 113 +" 53 2 75 B 83 R 54 b 117 r 32 + 3 119 C 43 S 66 c 104 s 90 + 4 49 D 46 T 124 d 101 t 44 +% 109 5 34 E 102 U 126 e 100 u 98 +& 72 6 82 F 40 V 59 f 69 v 60 +' 108 7 81 G 89 W 47 g 73 w 51 +( 70 8 95 H 38 X 92 h 99 x 33 +) 64 9 65 I 103 Y 71 i 63 y 97 +* 76 : 112 J 45 Z 115 j 94 z 62 ++ 67 ; 86 K 50 k 93 +, 116 < 118 L 42 l 39 +- 74 = 110 M 123 m 37 +. 68 > 122 N 91 n 61 +/ 87 ? 105 O 35 _ 56 o 48 +@end example + +@node Protocol +@chapter The CVS client/server protocol + +In the following, @samp{\n} refers to a linefeed and @samp{\t} refers to +a horizontal tab; @dfn{requests} are what the client sends and +@dfn{responses} are what the server sends. In general, the connection is +governed by the client---the server does not send responses without +first receiving requests to do so; see @ref{Response intro} for more +details of this convention. + +It is typical, early in the connection, for the client to transmit a +@code{Valid-responses} request, containing all the responses it +supports, followed by a @code{valid-requests} request, which elicits +from the server a @code{Valid-requests} response containing all the +requests it understands. In this way, the client and server each find +out what the other supports before exchanging large amounts of data +(such as file contents). + +@c Hmm, having 3 sections in this menu makes a certain amount of sense +@c but that structure gets lost in the printed manual (not sure about +@c HTML). Perhaps there is a better way. +@menu + +General protocol conventions: + +* Entries Lines:: Transmitting RCS data +* File Modes:: Read, write, execute, and possibly more... +* Filenames:: Conventions regarding filenames +* File transmissions:: How file contents are transmitted +* Strings:: Strings in various requests and responses +* Dates:: Times and dates + +The protocol itself: + +* Request intro:: General conventions relating to requests +* Requests:: List of requests +* Response intro:: General conventions relating to responses +* Response pathnames:: The "pathname" in responses +* Responses:: List of responses +* Text tags:: More details about the MT response + +An example session, and some further observations: + +* Example:: A conversation between client and server +* Requirements:: Things not to omit from an implementation +* Obsolete:: Former protocol features +@end menu + +@node Entries Lines +@section Entries Lines + +Entries lines are transmitted as: + +@example +/ @var{name} / @var{version} / @var{conflict} / @var{options} / @var{tag_or_date} +@end example + +@var{tag_or_date} is either @samp{T} @var{tag} or @samp{D} @var{date} +or empty. If it is followed by a slash, anything after the slash +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 +@var{conflict} does not start with a @samp{+}, it is silently ignored. + +@var{options} signifies the keyword expansion options (for example +@samp{-ko}). In an @code{Entry} request, this indicates the options +that were specified with the file from the previous file updating +response (@pxref{Response intro}, for a list of file updating +responses); if the client is specifying the @samp{-k} or @samp{-A} +option to @code{update}, then it is the server which figures out what +overrides what. + +@node File Modes +@section File Modes + +A mode is any number of repetitions of + +@example +@var{mode-type} = @var{data} +@end example + +separated by @samp{,}. + +@var{mode-type} is an identifier composed of alphanumeric characters. +Currently specified: @samp{u} for user, @samp{g} for group, @samp{o} +for other (see below for discussion of whether these have their POSIX +meaning or are more loose). Unrecognized values of @var{mode-type} +are silently ignored. + +@var{data} consists of any data not containing @samp{,}, @samp{\0} or +@samp{\n}. For @samp{u}, @samp{g}, and @samp{o} mode types, data +consists of alphanumeric characters, where @samp{r} means read, @samp{w} +means write, @samp{x} means execute, and unrecognized letters are +silently ignored. + +The two most obvious ways in which the mode matters are: (1) is it +writeable? This is used by the developer communication features, and +is implemented even on OS/2 (and could be implemented on DOS), whose +notion of mode is limited to a readonly bit. (2) is it executable? +Unix CVS users need CVS to store this setting (for shell scripts and +the like). The current CVS implementation on unix does a little bit +more than just maintain these two settings, but it doesn't really have +a nice general facility to store or version control the mode, even on +unix, much less across operating systems with diverse protection +features. So all the ins and outs of what the mode means across +operating systems haven't really been worked out (e.g. should the VMS +port use ACLs to get POSIX semantics for groups?). + +@node Filenames +@section Conventions regarding transmission of file names + +In most contexts, @samp{/} is used to separate directory and file +names in filenames, and any use of other conventions (for example, +that the user might type on the command line) is converted to that +form. The only exceptions might be a few cases in which the server +provides a magic cookie which the client then repeats verbatim, but as +the server has not yet been ported beyond unix, the two rules provide +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). + +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 +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} (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 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 Dates +@section Dates + +The protocol contains times and dates in various places. + +For the @samp{-D} option to the @code{annotate}, @code{co}, @code{diff}, +@code{export}, @code{history}, @code{rannotate}, @code{rdiff}, +@code{rtag}, @code{tag}, +and @code{update} requests, the server should support two formats: + +@example +26 May 1997 13:01:40 -0000 ; @r{RFC 822 as modified by RFC 1123} +5/26/1997 13:01:40 GMT ; @r{traditional} +@end example + +The former format is preferred; the latter however is sent by the CVS +command line client (versions 1.5 through at least 1.9). + +For the @samp{-d} option to the @code{log} and @code{rlog} requests, +servers should at +least support RFC 822/1123 format. Clients are encouraged to use this +format too (the command line CVS client, version 1.10 and older, just passed +along the date format specified by the user, however). + +The @code{Mod-time} response and @code{Checkin-time} request use RFC +822/1123 format (see the descriptions of that response and request for +details). + +For @code{Notify}, see the description of that request. + +@node Request intro +@section Request intro + +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. + +The term @dfn{command} means a request which expects a response (except +@code{valid-requests}). The general model is that the client transmits +a great number of requests, but nothing happens until the very end when +the client transmits a command. Although the intention is that +transmitting several commands in one connection should be legal, +existing servers probably have some bugs with some combinations of more +than one command, and so clients may find it necessary to make several +connections in some cases. This should be thought of as a workaround +rather than a desired attribute of the protocol. + +@node Requests +@section Requests + +Here are the requests: + +@table @code +@item Root @var{pathname} \n +Response expected: no. Tell the server which @code{CVSROOT} to use. +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. + +The @code{Root} request must be sent only once, and it must be sent +before any requests other than @code{Valid-responses}, +@code{valid-requests}, @code{UseUnchanged}, @code{Set}, +@code{Global_option}, @code{init}, @code{noop}, or @code{version}. + +@item Valid-responses @var{request-list} \n +Response expected: no. +Tell the server what responses the client will accept. +request-list is a space separated list of tokens. +The @code{Root} request need not have been previously sent. + +@item valid-requests \n +Response expected: yes. +Ask the server to send back a @code{Valid-requests} response. +The @code{Root} request need not have been previously sent. + +@item Directory @var{local-directory} \n +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. +The @var{local-directory} is relative to +the top level at which the command is occurring (i.e. the last +@code{Directory} which is sent before the command); +to indicate that top level, @samp{.} should be sent 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. + +Here's a more complex example, in which we request an update of a +working directory which has been checked out from multiple places in the +repository. + +@example +C: Argument dir1 +C: Directory dir1 +C: /home/foo/repos/mod1 +. . . +C: Argument dir2 +C: Directory dir2 +C: /home/foo/repos/mod2 +. . . +C: Argument dir3 +C: Directory dir3/subdir3 +C: /home/foo/repos/mod3 +. . . +C: update +@end example + +While directories @code{dir1} and @code{dir2} will be handled in similar +fashion to the other examples given above, @code{dir3} is slightly +different from the server's standpoint. Notice that module @code{mod3} +is actually checked out into @code{dir3/subdir3}, meaning that directory +@code{dir3} is either empty or does not contain data checked out from +this repository. + +The above example will work correctly in @sc{cvs} 1.10.1 and later. The +server will descend the tree starting from all directories mentioned in +@code{Argument} requests and update those directories specifically +mentioned in @code{Directory} requests. + +Previous versions of @sc{cvs} (1.10 and earlier) do not behave the same +way. While the descent of the tree begins at all directories mentioned +in @code{Argument} requests, descent into subdirectories only occurs if +a directory has been mentioned in a @code{Directory} request. +Therefore, the above example would succeed in updating @code{dir1} and +@code{dir2}, but would skip @code{dir3} because that directory was not +specifically mentioned in a @code{Directory} request. A functional +version of the above that would run on a 1.10 or earlier server is as +follows: + +@example +C: Argument dir1 +C: Directory dir1 +C: /home/foo/repos/mod1 +. . . +C: Argument dir2 +C: Directory dir2 +C: /home/foo/repos/mod2 +. . . +C: Argument dir3 +C: Directory dir3 +C: /home/foo/repos/. +. . . +C: Directory dir3/subdir3 +C: /home/foo/repos/mod3 +. . . +C: update +@end example + +Note the extra @code{Directory dir3} request. It might be better to use +@code{Emptydir} as the repository for the @code{dir3} directory, but the +above will certainly work. + +One more peculiarity of the 1.10 and earlier protocol is the ordering of +@code{Directory} arguments. In order for a subdirectory to be +registered correctly for descent by the recursion processor, its parent +must be sent first. For example, the following would not work to update +@code{dir3/subdir3}: + +@example +. . . +C: Argument dir3 +C: Directory dir3/subdir3 +C: /home/foo/repos/mod3 +. . . +C: Directory dir3 +C: /home/foo/repos/. +. . . +C: update +@end example + +The implementation of the server in 1.10 and earlier writes the +administration files for a given directory at the time of the +@code{Directory} request. It also tries to register the directory with +its parent to mark it for recursion. In the above example, at the time +@code{dir3/subdir3} is created, the physical directory for @code{dir3} +will be created on disk, but the administration files will not have been +created. Therefore, when the server tries to register +@code{dir3/subdir3} for recursion, the operation will silently fail +because the administration files do not yet exist for @code{dir3}. + +@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 +@code{Directory} request for @file{../../foo}, it must send a +@code{Max-dotdot} request with a @var{level} of at least 2. +@code{Max-dotdot} must be sent before the first @code{Directory} +request. + +@item Static-directory \n +Response expected: no. Tell the server that the directory most recently +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} +responses. + +@item Sticky @var{tagspec} \n +Response expected: no. Tell the server that the directory most recently +specified with @code{Directory} has a sticky tag or date @var{tagspec}. +The first character of @var{tagspec} is @samp{T} for a tag, @samp{D} +for a date, or some other character supplied by a Set-sticky response +from a previous request to the server. The remainder of @var{tagspec} +contains the actual tag or date, again as supplied by Set-sticky. + +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}. +Such a program would have been previously set with the +@code{Set-checkin-prog} response. + +@item Update-prog @var{program} \n +Response expected: no. Tell the server that the directory most recently +specified with @code{Directory} has an update program @var{program}. +Such a program would have been previously set with the +@code{Set-update-prog} response. + +@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{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{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 Kopt @var{option} \n +This indicates to the server which keyword expansion options to use for +the file specified by the next @code{Modified} or @code{Is-modified} +request (for example @samp{-kb} for a binary file). This is similar to +@code{Entry}, but is used for a file for which there is no entries line. +Typically this will be a file being added via an @code{add} or +@code{import} request. The client may not send both @code{Kopt} and +@code{Entry} for the same file. + +@item Checkin-time @var{time} \n +For the file specified by the next @code{Modified} request, use +@var{time} as the time of the checkin. The @var{time} is in the format +specified by RFC822 as modified by RFC1123. The client may specify any +timezone it chooses; servers will want to convert that to their own +timezone as appropriate. An example of this format is: + +@example +26 May 1997 13:01:40 -0400 +@end example + +There is no requirement that the client and server clocks be +synchronized. The client just sends its recommendation for a timestamp +(based on file timestamps or whatever), and the server should just believe +it (this means that the time might be in the future, for example). + +Note that this is not a general-purpose way to tell the server about the +timestamp of a file; that would be a separate request (if there are +servers which can maintain timestamp and time of checkin separately). + +This request should affect the @code{import} request, and may optionally +affect the @code{ci} request or other relevant requests if any. + +@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 +a file within the most recent directory sent with @code{Directory}; it +must not contain @samp{/}. 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 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 @var{filename} is +a file within the most recent directory sent with @code{Directory}; it +must not contain @samp{/}. + +@item UseUnchanged \n +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. +The @code{Root} request need not have been previously sent. + +@item Notify @var{filename} \n +Response expected: no. +Tell the server that an @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 +responses. +The @var{filename} is a file within the most recent directory sent with +@code{Directory}; it must not contain @samp{/}. +Additional data: +@example +@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, @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, zero or more of the following characters in the +following order: @samp{E} for edit, @samp{U} for unedit, @samp{C} for +commit, and all other letters should be silently ignored for future +expansion. If @var{notification-type} is @samp{E} the temporary watches +are set; if it is @samp{U} they are cleared. +If @var{watches} is followed by \t then the +\t and the rest of the line should be ignored, for future expansion. + +The @var{time}, @var{clienthost}, and @var{working-dir} fields may not +contain the characters @samp{+}, @samp{,}, @samp{>}, @samp{;}, or @samp{=}. + +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. @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 +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). + +Though this request will be supported into the forseeable future, it has been +the source of numerous bug reports in the past due to the complexity of testing +this functionality via the test suite and client developers are encouraged not +to use it. Instead, please consider munging conflicting names and maintaining +a map for communicating with the server. For example, suppose the server sends +files @file{case}, @file{CASE}, and @file{CaSe}. The client could write all +three files to names such as, @file{case}, @file{case_prefix_case}, and +@file{case_prefix_2_case} and maintain a mapping between the file names in, for +instance a new @file{CVS/Map} file. + +@item Argument @var{text} \n +Response expected: no. +Save argument for use in a subsequent command. Arguments +accumulate until an argument-using command is given, at which point +they are forgotten. + +@item Argumentx @var{text} \n +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 +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. +The @code{Root} request need not have been previously sent. + +@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. + +Note that this request does not fully prevent an attacker from hijacking +the connection, in the sense that it does not prevent hijacking the +connection between the initial authentication and the +@code{Kerberos-encrypt} request. + +@item Gssapi-encrypt \n +Response expected: no. +Use GSSAPI encryption to encrypt all further communication between the +client and the server. This will only work if the connection was made +over GSSAPI in the first place. See @code{Kerberos-encrypt}, above, for +the relation between @code{Gssapi-encrypt} and @code{Gzip-stream}. + +Note that this request does not fully prevent an attacker from hijacking +the connection, in the sense that it does not prevent hijacking the +connection between the initial authentication and the +@code{Gssapi-encrypt} request. + +@item Gssapi-authenticate \n +Response expected: no. +Use GSSAPI authentication to authenticate all further communication +between the client and the server. This will only work if the +connection was made over GSSAPI in the first place. Encrypted data is +automatically authenticated, so using both @code{Gssapi-authenticate} +and @code{Gssapi-encrypt} has no effect beyond that of +@code{Gssapi-encrypt}. Unlike encrypted data, it is reasonable to +compress authenticated data. + +Note that this request does not fully prevent an attacker from hijacking +the connection, in the sense that it does not prevent hijacking the +connection between the initial authentication and the +@code{Gssapi-authenticate} request. + +@item Set @var{variable}=@var{value} \n +Response expected: no. +Set a user variable @var{variable} to @var{value}. +The @code{Root} request need not have been previously sent. + +@item expand-modules \n +Response expected: yes. Expand the modules which are specified in the +arguments. Returns the data in @code{Module-expansion} responses. Note +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. + +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 list \n +@itemx tag \n +@itemx status \n +@itemx admin \n +@itemx history \n +@itemx watchers \n +@itemx editors \n +@itemx annotate \n +Response expected: yes. Actually do a cvs command. 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. 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. + +@item log \n +Response expected: yes. Show information for past revisions. This uses +any previous @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. Also uses +previous @code{Argument}'s of which the canonical forms are the +following (@sc{cvs} 1.10 and older clients sent what the user specified, +but clients are encouraged to use the canonical forms and other forms +are deprecated): + +@table @code +@item -b, -h, -l, -N, -R, -t +These options go by themselves, one option per @code{Argument} request. + +@item -d @var{date1}<@var{date2} +Select revisions between @var{date1} and @var{date2}. Either date +may be omitted in which case there is no date limit at that end of the +range (clients may specify dates such as 1 Jan 1970 or 1 Jan 2038 for +similar purposes but this is problematic as it makes assumptions about +what dates the server supports). Dates are in RFC822/1123 format. The +@samp{-d} is one @code{Argument} request and the date range is a second +one. + +@item -d @var{date1}<=@var{date2} +Likewise but compare dates for equality. + +@item -d @var{singledate} +Select the single, latest revision dated @var{singledate} or earlier. + +To include several date ranges and/or singledates, repeat the @samp{-d} +option as many times as necessary. + +@item -r@var{rev1}:@var{rev2} +@itemx -r@var{branch} +@itemx -r@var{branch}. +@itemx -r +Specify revisions (note that @var{rev1} or @var{rev2} can be omitted, or +can refer to branches). Send both the @samp{-r} and the revision +information in a single @code{Argument} request. To include several +revision selections, repeat the @samp{-r} option. + +@item -s @var{state} +@itemx -w +@itemx -w@var{login} +Select on states or users. To include more than one state or user, +repeat the option. Send the @samp{-s} option as a separate argument +from the state being selected. Send the @samp{-w} option as part of the +same argument as the user being selected. +@end table + +@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 export \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, as described for the @code{co} request. The +intention behind this command is that a client can get sources from a +server without storing CVS information about those sources. That is, a +client probably should not count on being able to take the entries line +returned in the @code{Created} response from an @code{export} request +and send it in a future @code{Entry} request. Note that the entries +line in the @code{Created} response must indicate whether the file is +binary or text, so the client can create it correctly. + +@item ls \n +@itemx rannotate \n +@itemx rdiff \n +@itemx rlist \n +@itemx rlog \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 these commands; they are not used. Arguments to these +commands are module names, as described for @code{co}. @code{ls} is a +synonym for @code{rlist}, for compatibility with CVSNT. + +@item init @var{root-name} \n +Response expected: yes. If it doesn't already exist, create a @sc{cvs} +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. + +@item update \n +Response expected: yes. Actually do a @code{cvs update} command. 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. 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{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 - unlike most commands, the repository field of each +@code{Directory} request is ignored (it merely must point somewhere +within the root). 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 with CVS 1.9 and older, +a pathname specified in +an @code{Argument} request cannot contain @samp{/}. There is no good +reason for this restriction, and in fact more recent CVS servers don't +have it. +But the way to interoperate with the older servers 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 from the buggy servers. + +@item remove \n +Response expected: yes. Remove a file. 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. + +Note that this request does not actually do anything to the repository; +the only effect of a successful @code{remove} request is to supply the +client with a new entries line containing @samp{-} to indicate a removed +file. In fact, the client probably could perform this operation without +contacting the server, although using @code{remove} may cause the server +to perform a few more checks. + +The client sends a subsequent @code{ci} request to actually record the +removal in the repository. + +@item watch-on \n +@itemx watch-off \n +@itemx watch-add \n +@itemx watch-remove \n +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{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 +Response expected: yes. Note that a @code{cvs release} command has +taken place and update the history file accordingly. + +@item global-list-quiet \n +Response expected: yes. This request is a synonym for noop, but its existance +notifies the client that a @code{-q} option to @code{list} and @code{rlist} +will be rejected. This, in a reverse-logic sort of way, is here so that when +it @emph{isn't} received, as for instance from CVSNT, the client will know that +the quiet option has to be sent as a command option rather than a global +option. + +@item noop \n +Response expected: yes. This request is a null command in the sense +that it doesn't do anything, but merely (as with any other requests +expecting a response) sends back any responses pertaining to pending +errors, pending @code{Notified} responses, etc. +The @code{Root} request need not have been previously sent. + +@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 +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 +compressed) not to do the compression. Compression is indicated by a +@samp{z} preceding the file length. + +Availability of this request in the server indicates to the client that +it may compress files sent to the server, regardless of whether the +client actually uses this request. + +@item wrapper-sendme-rcsOptions \n +Response expected: yes. +Request that the server transmit mappings from filenames to keyword +expansion modes in @code{Wrapper-rcsOption} responses. + +@item version \n +Response expected: yes. +Request that the server transmit its version message. +The @code{Root} request need not have been previously sent. + +@item @var{other-request} @var{text} \n +Response expected: yes. +Any unrecognized request expects a response, and does not +contain any additional data. The response will normally be something like +@samp{error unrecognized request}, but it could be a different error if +a previous request which doesn't expect a response produced an error. +@end table + +When the client is done, it drops the connection. + +@node Response intro +@section Introduction to Responses + +After a command which expects a response, the server sends however many +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). + +Any set of responses always ends with @samp{error} or @samp{ok}. This +indicates that the response is over. + +@c "file updating response" and "file update modifying response" are +@c lame terms (mostly because they are so awkward). Any better ideas? +The responses @code{Checked-in}, @code{New-entry}, @code{Updated}, +@code{Created}, @code{Update-existing}, @code{Merged}, and +@code{Patched} are refered to as @dfn{file updating} responses, because +they change the status of a file in the working directory in some way. +The responses @code{Mode}, @code{Mod-time}, and @code{Checksum} are +referred to as @dfn{file update modifying} responses because they modify +the next file updating response. In no case shall a file update +modifying response apply to a file updating response other than the next +one. Nor can the same file update modifying response occur twice for +a given file updating response (if servers diagnose this problem, it may +aid in detecting the case where clients send an update modifying +response without following it by a file updating response). + +@node Response pathnames +@section The "pathname" in responses + +Many of the responses contain something called @var{pathname}. +@c FIXME: should better document when the specified repository needs to +@c end in "/.". +The name is somewhat misleading; it 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. + +@node Responses +@section Responses + +Here are the responses: + +@table @code +@item Valid-requests @var{request-list} \n +Indicate what requests the server will accept. @var{request-list} +is a space separated list of tokens. If the server supports sending +patches, it will include @samp{update-patches} in this list. The +@samp{update-patches} request does not actually do anything. + +@item Checked-in @var{pathname} \n +Additional data: New Entries line, \n. This means a file @var{pathname} +has been successfully operated on (checked in, added, etc.). name in +the Entries line is the same as the last component of @var{pathname}. + +@item New-entry @var{pathname} \n +Additional data: New Entries line, \n. Like @code{Checked-in}, but the +file is not up to date. + +@item Updated @var{pathname} \n +Additional data: New Entries line, \n, mode, \n, file transmission. A +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. 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, +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. + +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 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 +@code{Checked-in}. @code{Mode} is a file update modifying response +as described in @ref{Response intro}. + +@item Mod-time @var{time} \n +Set the modification time of the next file sent to @var{time}. +@code{Mod-time} is a file update modifying response +as described in @ref{Response intro}. +The +@var{time} is in the format specified by RFC822 as modified by RFC1123. +The server may specify any timezone it chooses; clients will want to +convert that to their own timezone as appropriate. An example of this +format is: + +@example +26 May 1997 13:01:40 -0400 +@end example + +There is no requirement that the client and server clocks be +synchronized. The server just sends its recommendation for a timestamp +(based on its own clock, presumably), and the client should just believe +it (this means that the time might be in the future, for example). + +If the server does not send @code{Mod-time} for a given file, the client +should pick a modification time in the usual way (usually, just let the +operating system set the modification time to the time that the CVS +command is running). + +@item Checksum @var{checksum}\n +The @var{checksum} applies to the next file sent (that is, +@code{Checksum} is a file update modifying response +as described in @ref{Response intro}). +In the case of +@code{Patched}, the checksum applies to the file after being patched, +not to the patch itself. The client should compute the checksum itself, +after receiving the file or patch, and signal an error if the checksums +do not match. The checksum is the 128 bit MD5 checksum represented as +32 hex digits (MD5 is described in RFC1321). +This response is optional, and is only used if the +client supports it (as judged by the @code{Valid-responses} request). + +@item Copy-file @var{pathname} \n +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}). + +@item Remove-entry @var{pathname} \n +The file needs its entry removed from @code{CVS/Entries}, but the file +itself is already gone (this happens in response to a @code{ci} request +which involves committing the removal of a file). + +@item Set-static-directory @var{pathname} \n +This instructs the client to set the @code{Entries.Static} flag, which +it should then send back to the server in a @code{Static-directory} +request whenever the directory is operated on. @var{pathname} ends in a +slash; its purpose is to specify a directory, not a file within a +directory. + +@item Clear-static-directory @var{pathname} \n +Like @code{Set-static-directory}, but clear, not set, the flag. + +@item Set-sticky @var{pathname} \n +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 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 +Clear any sticky tag or date set by @code{Set-sticky}. + +@item Template @var{pathname} \n +Additional data: file transmission (note: compressed file transmissions +are not supported). @var{pathname} ends in a slash; its purpose is to +specify a directory, not a file within a directory. Tell the client to +store the file transmission as the template log message, and then use +that template in the future when prompting the user for a log message. + +@item Set-checkin-prog @var{dir} \n +Additional data: @var{prog} \n. Tell the client to set a checkin +program, which should be supplied with the @code{Checkin-prog} request +for future operations. + +@item Set-update-prog @var{dir} \n +Additional data: @var{prog} \n. Tell the client to set an update +program, which should be supplied with the @code{Update-prog} request +for future operations. + +@item Notified @var{pathname} \n +Indicate to the client that the notification for @var{pathname} has been +done. There should be one such response for every @code{Notify} +request; if there are several @code{Notify} requests for a single file, +the requests should be processed in order; the first @code{Notified} +response pertains to the first @code{Notify} request, etc. + +@item Module-expansion @var{pathname} \n +Return a file or directory +which is included in a particular module. @var{pathname} is relative +to cvsroot, unlike most pathnames in responses. @var{pathname} should +be used to look and see whether some or all of the module exists on +the client side; it is not necessarily suitable for passing as an +argument to a @code{co} request (for example, if the modules file +contains the @samp{-d} option, it will be the directory specified with +@samp{-d}, not the name of the module). + +@item Wrapper-rcsOption @var{pattern} -k '@var{option}' \n +Transmit to the client a filename pattern which implies a certain +keyword expansion mode. The @var{pattern} is a wildcard pattern (for +example, @samp{*.exe}. The @var{option} is @samp{b} for binary, and so +on. Note that although the syntax happens to resemble the syntax in +certain CVS configuration files, it is more constrained; there must be +exactly one space between @var{pattern} and @samp{-k} and exactly one +space between @samp{-k} and @samp{'}, and no string is permitted in +place of @samp{-k} (extensions should be done with new responses, not by +extending this one, for graceful handling of @code{Valid-responses}). + +@item M @var{text} \n +A one-line message for the user. +Note that the format of @var{text} is not designed for machine parsing. +Although sometimes scripts and clients will have little choice, the +exact text which is output is subject to vary at the discretion of the +server and the example output given in this document is just that, +example output. Servers are encouraged to use the @samp{MT} response, +and future versions of this document will hopefully standardize more of +the @samp{MT} tags; see @ref{Text tags}. + +@item Mbinary \n +Additional data: file transmission (note: compressed file transmissions +are not supported). This is like @samp{M}, except the contents of the +file transmission are binary and should be copied to standard output +without translation to local text file conventions. To transmit a text +file to standard output, servers should use a series of @samp{M} requests. + +@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 MT @var{tagname} @var{data} \n + +This response provides for tagged text. It is similar to +SGML/HTML/XML in that the data is structured and a naive application +can also make some sense of it without understanding the structure. +The syntax is not SGML-like, however, in order to fit into the CVS +protocol better and (more importantly) to make it easier to parse, +especially in a language like perl or awk. + +The @var{tagname} can have several forms. If it starts with @samp{a} +to @samp{z} or @samp{A} to @samp{Z}, then it represents tagged text. +If the implementation recognizes @var{tagname}, then it may interpret +@var{data} in some particular fashion. If the implementation does not +recognize @var{tagname}, then it should simply treat @var{data} as +text to be sent to the user (similar to an @samp{M} response). There +are two tags which are general purpose. The @samp{text} tag is +similar to an unrecognized tag in that it provides text which will +ordinarily be sent to the user. The @samp{newline} tag is used +without @var{data} and indicates that a newline will ordinarily be +sent to the user (there is no provision for embedding newlines in the +@var{data} of other tagged text responses). + +If @var{tagname} starts with @samp{+} it indicates a start tag and if +it starts with @samp{-} it indicates an end tag. The remainder of +@var{tagname} should be the same for matching start and end tags, and +tags should be nested (for example one could have tags in the +following order @code{+bold} @code{+italic} @code{text} @code{-italic} +@code{-bold} but not @code{+bold} @code{+italic} @code{text} +@code{-bold} @code{-italic}). A particular start and end tag may be +documented to constrain the tagged text responses which are valid +between them. + +Note that if @var{data} is present there will always be exactly one +space between @var{tagname} and @var{data}; if there is more than one +space, then the spaces beyond the first are part of @var{data}. + +Here is an example of some tagged text responses. Note that there is +a trailing space after @samp{Checking in} and @samp{initial revision:} +and there are two trailing spaces after @samp{<--}. Such trailing +spaces are, of course, part of @var{data}. + +@example +MT +checking-in +MT text Checking in +MT fname gz.tst +MT text ; +MT newline +MT rcsfile /home/kingdon/zwork/cvsroot/foo/gz.tst,v +MT text <-- +MT fname gz.tst +MT newline +MT text initial revision: +MT init-rev 1.1 +MT newline +MT text done +MT newline +MT -checking-in +@end example + +If the client does not support the @samp{MT} response, the same +responses might be sent as: + +@example +M Checking in gz.tst; +M /home/kingdon/zwork/cvsroot/foo/gz.tst,v <-- gz.tst +M initial revision: 1.1 +M done +@end example + +For a list of specific tags, see @ref{Text tags}. + +@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 +feature, or if it's not appropriate for this particular message, it just +omits the errno-code (in that case there are two spaces after +@samp{error}). Text is an error message such as that provided by +strerror(), or any other message the server wants to use. +The @var{text} is like the @code{M} response, in the sense that it is +not particularly intended to be machine-parsed; servers may wish to +print an error message with @code{MT} responses, and then issue a +@code{error} response without @var{text} (although it should be noted +that @code{MT} currently has no way of flagging the output as intended +for standard error, the way that the @code{E} response does). + +@item ok \n +The command completed successfully. +@end table + +@node Text tags +@section Tags for the MT tagged text response + +The @code{MT} response, as described in @ref{Responses}, offers a +way for the server to send tagged text to the client. This section +describes specific tags. The intention is to update this section as +servers add new tags. + +In the following descriptions, @code{text} and @code{newline} tags are +omitted. Such tags contain information which is intended for users (or +to be discarded), and are subject to change at the whim of the server. +To avoid being vulnerable to such whim, clients should look for the tags +listed here, not @code{text}, @code{newline}, or other tags. + +The following tag means to indicate to the user that a file has been +updated. It is more or less redundant with the @code{Created} and +@code{Update-existing} responses, but we don't try to specify here +whether it occurs in exactly the same circumstances as @code{Created} +and @code{Update-existing}. The @var{name} is the pathname of the file +being updated relative to the directory in which the command is +occurring (that is, the last @code{Directory} request which is sent +before the command). + +@example +MT +updated +MT fname @var{name} +MT -updated +@end example + +The @code{importmergecmd} tag is used when doing an import which has +conflicts. The client can use it to report how to merge in the newly +imported changes. The @var{count} is the number of conflicts. The +newly imported changes can be merged by running the following command: +@smallexample +cvs checkout -j @var{tag1} -j @var{tag2} @var{repository} +@end smallexample + +@example +MT +importmergecmd +MT conflicts @var{count} +MT mergetag1 @var{tag1} +MT mergetag2 @var{tag2} +MT repository @var{repository} +MT -importmergecmd +@end example + +@node Example +@section Example + +@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 RFC2234 BNF (I fear that would be less clear than +@c what we do now, however). Plus what about RFC2119 terminology (MUST, +@c SHOULD, &c) 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 +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 +(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. + +@node Protocol Notes +@chapter Notes on the Protocol + +A number of enhancements are possible. Also see the file @sc{todo} in +the @sc{cvs} source distribution, which has further ideas concerning +various aspects of @sc{cvs}, some of which impact the protocol. +Similarly, the @code{http://www.cvshome.org} site, in particular the +@cite{Development} pages. + +@itemize @bullet +@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; 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 +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). + +@item +The scrambling algorithm in the CVS client and server actually support +more characters than those documented in @ref{Password scrambling}. +Someday we are going to either have to document them all (but this is +not as easy as it may look, see below), or (gradually and with adequate +process) phase out the support for other characters in the CVS +implementation. This business of having the feature partly undocumented +isn't a desirable state long-term. + +The problem with documenting other characters is that unless we know +what character set is in use, there is no way to make a password +portable from one system to another. For example, a with a circle on +top might have different encodings in different character sets. + +It @emph{almost} works to say that the client picks an arbitrary, +unknown character set (indeed, having the CVS client know what character +set the user has in mind is a hard problem otherwise), and scrambles +according to a certain octet<->octet mapping. There are two problems +with this. One is that the protocol has no way to transmit character 10 +decimal (linefeed), and the current server and clients have no way to +handle 0 decimal (NUL). This may cause problems with certain multibyte +character sets, in which octets 10 and 0 will appear in the middle of +other characters. The other problem, which is more minor and possibly +not worth worrying about, is that someone can type a password on one +system and then go to another system which uses a different encoding for +the same characters, and have their password not work. + +The restriction to the ISO646 invariant subset is the best approach for +strings which are not particularly significant to users. Passwords are +visible enough that this is somewhat doubtful as applied here. ISO646 +does, however, have the virtue (!?) of offending everyone. It is easy +to say "But the $ is right on people's keyboards! Surely we can't +forbid that". From a human factors point of view, that makes quite a +bit of sense. The contrary argument, of course, is that a with a circle +on top, or some of the characters poorly handled by Unicode, are on +@emph{someone}'s keyboard. + +@end itemize + +@bye diff --git a/contrib/cvs-1.12.9/doc/version-client.texi b/contrib/cvs-1.12.9/doc/version-client.texi new file mode 100644 index 0000000000..41b144fdc8 --- /dev/null +++ b/contrib/cvs-1.12.9/doc/version-client.texi @@ -0,0 +1,4 @@ +@set UPDATED 23 April 2004 +@set UPDATED-MONTH April 2004 +@set EDITION 1.12.9 +@set VERSION 1.12.9 diff --git a/contrib/cvs-1.12.9/doc/version.texi b/contrib/cvs-1.12.9/doc/version.texi new file mode 100644 index 0000000000..6d19c91f43 --- /dev/null +++ b/contrib/cvs-1.12.9/doc/version.texi @@ -0,0 +1,4 @@ +@set UPDATED 19 May 2004 +@set UPDATED-MONTH May 2004 +@set EDITION 1.12.9 +@set VERSION 1.12.9 diff --git a/contrib/cvs-1.12.9/lib/README b/contrib/cvs-1.12.9/lib/README new file mode 100644 index 0000000000..ce1cc7f8d1 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/README @@ -0,0 +1,9 @@ +Many of the source files in this directory come from the GNULIB project +/, and, +if they don't, they should. + +What this means is that there is often at least one corresponding Autoconf +macro in the CVS/m4 directory and that bug fixes and enhancements to this code +should be sent to the GNULIB project and then reimported here after the GNULIB +developers approve and adopt the change. Changes should not be made locally +without good reason! diff --git a/contrib/cvs-1.12.9/lib/argmatch.c b/contrib/cvs-1.12.9/lib/argmatch.c new file mode 100644 index 0000000000..90b44c6e0b --- /dev/null +++ b/contrib/cvs-1.12.9/lib/argmatch.c @@ -0,0 +1,85 @@ +/* argmatch.c -- find a match for a string in an array + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* Written by David MacKenzie */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#ifdef STDC_HEADERS +#include +#endif + +extern char *program_name; + +/* If ARG is an unambiguous match for an element of the + null-terminated array OPTLIST, return the index in OPTLIST + of the matched element, else -1 if it does not match any element + or -2 if it is ambiguous (is a prefix of more than one element). */ + +int +argmatch (arg, optlist) + char *arg; + char **optlist; +{ + int i; /* Temporary index in OPTLIST. */ + size_t arglen; /* Length of ARG. */ + int matchind = -1; /* Index of first nonexact match. */ + int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ + + arglen = strlen (arg); + + /* Test all elements for either exact match or abbreviated matches. */ + for (i = 0; optlist[i]; i++) + { + if (!strncmp (optlist[i], arg, arglen)) + { + if (strlen (optlist[i]) == arglen) + /* Exact match found. */ + return i; + else if (matchind == -1) + /* First nonexact match found. */ + matchind = i; + else + /* Second nonexact match found. */ + ambiguous = 1; + } + } + if (ambiguous) + return -2; + else + return matchind; +} + +/* Error reporting for argmatch. + KIND is a description of the type of entity that was being matched. + VALUE is the invalid value that was given. + PROBLEM is the return value from argmatch. */ + +void +invalid_arg (kind, value, problem) + char *kind; + char *value; + int problem; +{ + fprintf (stderr, "%s: ", program_name); + if (problem == -1) + fprintf (stderr, "invalid"); + else /* Assume -2. */ + fprintf (stderr, "ambiguous"); + fprintf (stderr, " %s `%s'\n", kind, value); +} diff --git a/contrib/cvs-1.12.9/lib/asnprintf.c b/contrib/cvs-1.12.9/lib/asnprintf.c new file mode 100644 index 0000000000..4881a9238f --- /dev/null +++ b/contrib/cvs-1.12.9/lib/asnprintf.c @@ -0,0 +1,37 @@ +/* Formatted output to strings. + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "vasnprintf.h" + +#include + +char * +asnprintf (char *resultbuf, size_t *lengthp, const char *format, ...) +{ + va_list args; + char *result; + + va_start (args, format); + result = vasnprintf (resultbuf, lengthp, format, args); + va_end (args); + return result; +} diff --git a/contrib/cvs-1.12.9/lib/basename.c b/contrib/cvs-1.12.9/lib/basename.c new file mode 100644 index 0000000000..5ff2989330 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/basename.c @@ -0,0 +1,79 @@ +/* basename.c -- return the last element in a path + + Copyright (C) 1990, 1998, 1999, 2000, 2001, 2003 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "dirname.h" +#include + +/* In general, we can't use the builtin `basename' function if available, + since it has different meanings in different environments. + In some environments the builtin `basename' modifies its argument. + + Return the address of the last file name component of NAME. If + NAME has no file name components because it is all slashes, return + NAME if it is empty, the address of its last slash otherwise. */ + +char * +base_name (char const *name) +{ + char const *base = name + FILESYSTEM_PREFIX_LEN (name); + char const *p; + + for (p = base; *p; p++) + { + if (ISSLASH (*p)) + { + /* Treat multiple adjacent slashes like a single slash. */ + do p++; + while (ISSLASH (*p)); + + /* If the file name ends in slash, use the trailing slash as + the basename if no non-slashes have been found. */ + if (! *p) + { + if (ISSLASH (*base)) + base = p - 1; + break; + } + + /* *P is a non-slash preceded by a slash. */ + base = p; + } + } + + return (char *) base; +} + +/* Return the length of of the basename NAME. Typically NAME is the + value returned by base_name. Act like strlen (NAME), except omit + redundant trailing slashes. */ + +size_t +base_len (char const *name) +{ + size_t len; + + for (len = strlen (name); 1 < len && ISSLASH (name[len - 1]); len--) + continue; + + return len; +} diff --git a/contrib/cvs-1.12.9/lib/dirname.h b/contrib/cvs-1.12.9/lib/dirname.h new file mode 100644 index 0000000000..62da37dc9a --- /dev/null +++ b/contrib/cvs-1.12.9/lib/dirname.h @@ -0,0 +1,43 @@ +/* Take file names apart into directory and base names. + + Copyright (C) 1998, 2001, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef DIRNAME_H_ +# define DIRNAME_H_ 1 + +# include + +# ifndef DIRECTORY_SEPARATOR +# define DIRECTORY_SEPARATOR '/' +# endif + +# ifndef ISSLASH +# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR) +# endif + +# ifndef FILESYSTEM_PREFIX_LEN +# define FILESYSTEM_PREFIX_LEN(Filename) 0 +# endif + +char *base_name (char const *path); +char *dir_name (char const *path); +size_t base_len (char const *path); +size_t dir_len (char const *path); + +int strip_trailing_slashes (char *path); + +#endif /* not DIRNAME_H_ */ diff --git a/contrib/cvs-1.12.9/lib/error.h b/contrib/cvs-1.12.9/lib/error.h new file mode 100644 index 0000000000..84d321aebd --- /dev/null +++ b/contrib/cvs-1.12.9/lib/error.h @@ -0,0 +1,49 @@ +/* error.h -- declaration for error-reporting function + Copyright (C) 1995 Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +#ifndef ERROR_H +#define ERROR_H + +/* Add GNU attribute suppport. */ +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# else +# if __GNUC__ == 2 && __GNUC_MINOR__ < 96 +# define __pure__ /* empty */ +# endif +# if __GNUC__ < 3 +# define __malloc__ /* empty */ +# endif +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __const__ const +# define __format__ format +# define __noreturn__ noreturn +# define __printf__ printf +# endif +#endif + +void error (int, int, const char *, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); + +/* 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 + process and packages up its stderr in the protocol. */ +extern int error_use_protocol; + +#endif /* ERROR_H */ diff --git a/contrib/cvs-1.12.9/lib/exit.h b/contrib/cvs-1.12.9/lib/exit.h new file mode 100644 index 0000000000..4e8d465165 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/exit.h @@ -0,0 +1,32 @@ +/* exit() function. + Copyright (C) 1995, 2001 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _EXIT_H +#define _EXIT_H + +/* Get exit() declaration. */ +#include + +/* Some systems do not define EXIT_*, even with STDC_HEADERS. */ +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif +#ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +#endif /* _EXIT_H */ diff --git a/contrib/cvs-1.12.9/lib/exitfail.c b/contrib/cvs-1.12.9/lib/exitfail.c new file mode 100644 index 0000000000..2ae5f69555 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/exitfail.c @@ -0,0 +1,27 @@ +/* Failure exit status + + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "exitfail.h" +#include "exit.h" + +int volatile exit_failure = EXIT_FAILURE; diff --git a/contrib/cvs-1.12.9/lib/exitfail.h b/contrib/cvs-1.12.9/lib/exitfail.h new file mode 100644 index 0000000000..cf5ab71958 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/exitfail.h @@ -0,0 +1,20 @@ +/* Failure exit status + + Copyright (C) 2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +extern int volatile exit_failure; diff --git a/contrib/cvs-1.12.9/lib/getdate.y b/contrib/cvs-1.12.9/lib/getdate.y new file mode 100644 index 0000000000..add7c62ce7 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getdate.y @@ -0,0 +1,1030 @@ +%{ +/* +** Originally written by Steven M. Bellovin while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** and Jim Berets in August, 1990; +** +** This grammar has 10 shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#ifdef HAVE_CONFIG_H +#if defined (emacs) || defined (CONFIG_BROKETS) +#include +#else +#include "config.h" +#endif +#endif + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +#undef static +#endif + +/* AIX seems to have problems with #include and _LARGE_FILES */ +#include + +/* The code at the top of get_date which figures out the offset of the + current time zone checks various CPP symbols to see if special + tricks are need, but defaults to using the gettimeofday system call. + Include if that will be used. */ + +#if defined(vms) +# include +#else /* defined(vms) */ +# include +#endif /* !defined(vms) */ +# include "xtime.h" + +#if defined (STDC_HEADERS) || defined (USG) +#include +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +#define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +#if defined (STDC_HEADERS) +#include +#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(); + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yyparse (); +static int yylex (); +static int yyerror (); + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST + +%type tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | cvsstamp { + yyHaveTime++; + yyHaveDate++; + yyHaveZone++; + } + | number + ; + +cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER { + yyYear = $1; + if (yyYear < 100) yyYear += 1900; + yyMonth = $3; + yyDay = $5; + yyHour = $7; + yyMinutes = $9; + yySeconds = $11; + yyDSTmode = DSToff; + yyTimezone = 0; + } + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + if ($1 >= 100) { + yyYear = $1; + yyMonth = $3; + yyDay = $5; + } else { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL } +}; + + + + +/* ARGSUSED */ +static int +yyerror(s) + char *s; +{ + return 0; +} + + +static time_t +ToSeconds(Hours, Minutes, Seconds, Meridian) + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + + +/* Year is either + * A negative number, which means to use its absolute value (why?) + * A number from 0 to 99, which means a year from 1900 to 1999, or + * The actual year (>=100). */ +static time_t +Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) + time_t Month; + time_t Day; + time_t Year; + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; + DSTMODE DSTmode; +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 0) + Year = -Year; + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + /* 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]) + /* FIXME: + * It would be nice to set a global error string here. + * "February 30 is not a valid date" is much more informative than + * "Can't parse date/time: 100 months" when the user input was + * "100 months" and addition resolved that to February 30, for + * example. See rcs2-7 in src/sanity.sh for more. */ + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(Start, Future) + time_t Start; + time_t Future; +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(Start, DayOrdinal, DayNumber) + time_t Start; + time_t DayOrdinal; + time_t DayNumber; +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(Start, RelMonth) + time_t Start; + time_t RelMonth; +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(buff) + char *buff; +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex() +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static long +difftm (a, b) + struct tm *a, *b; +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + int days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (long)(ay-by) * 365 + ); + return (60*(60*(24*days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t +get_date(p, now) + char *p; + struct timeb *now; +{ + struct tm *tm, gmt; + struct timeb ftz; + time_t Start; + time_t tod; + time_t nowtime; + + yyInput = p; + if (now == NULL) { + struct tm *gmt_ptr; + + now = &ftz; + (void)time (&nowtime); + + gmt_ptr = gmtime (&nowtime); + if (gmt_ptr != NULL) + { + /* Make a copy, in case localtime modifies *tm (I think + that comment now applies to *gmt_ptr, but I am too + lazy to dig into how gmtime and locatime allocate the + structures they return pointers to). */ + gmt = *gmt_ptr; + } + + if (! (tm = localtime (&nowtime))) + return -1; + + if (gmt_ptr != NULL) + ftz.timezone = difftm (&gmt, tm) / 60; + else + /* We are on a system like VMS, where the system clock is + in local time and the system has no concept of timezones. + Hopefully we can fake this out (for the case in which the + user specifies no timezone) by just saying the timezone + is zero. */ + ftz.timezone = 0; + + if(tm->tm_isdst) + ftz.timezone += 60; + } + else + { + nowtime = now->time; + } + + tm = localtime(&nowtime); + yyYear = tm->tm_year + 1900; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = now->timezone; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = nowtime; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +int +main(ac, av) + int ac; + char *av[]; +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff, (struct timeb *)NULL); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/contrib/cvs-1.12.9/lib/getline.c b/contrib/cvs-1.12.9/lib/getline.c new file mode 100644 index 0000000000..e5eb43701d --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getline.c @@ -0,0 +1,54 @@ +/* getline.c -- Replacement for GNU C library function getline + + Copyright (C) 1993, 1996, 1997, 1998, 2000, 2003 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Jan Brittenson, bson@gnu.ai.mit.edu. */ + +#if HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "getline.h" + +#if defined __GNU_LIBRARY__ && HAVE_GETDELIM + +ssize_t +getline (char **lineptr, size_t *linesize, FILE *stream) +{ + return getdelim (lineptr, linesize, '\n', stream); +} + +#else /* ! have getdelim */ + +# include "getndelim2.h" + +ssize_t +getline (char **lineptr, size_t *linesize, FILE *stream) +{ + return getndelim2 (lineptr, linesize, 0, GETNDELIM_NO_LIMIT, '\n', 0, + stream); +} + +ssize_t +getdelim (char **lineptr, size_t *linesize, int delimiter, FILE *stream) +{ + return getndelim2 (lineptr, linesize, 0, GETNDELIM_NO_LIMIT, delimiter, 0, + stream); +} +#endif diff --git a/contrib/cvs-1.12.9/lib/getline.h b/contrib/cvs-1.12.9/lib/getline.h new file mode 100644 index 0000000000..ee9fc052c1 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getline.h @@ -0,0 +1,39 @@ +/* Replacement for GNU C library function getline + + Copyright (C) 1995, 1997, 1999, 2000, 2001, 2002, 2003 Free + Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef GETLINE_H_ +# define GETLINE_H_ 1 + +# include +# include + +/* Get ssize_t. */ +# include + +/* glibc2 has these functions declared in . Avoid redeclarations. */ +# if __GLIBC__ < 2 + +extern ssize_t getline (char **_lineptr, size_t *_linesize, FILE *_stream); + +extern ssize_t getdelim (char **_lineptr, size_t *_linesize, int _delimiter, + FILE *_stream); + +# endif + +#endif /* not GETLINE_H_ */ diff --git a/contrib/cvs-1.12.9/lib/getndelim2.c b/contrib/cvs-1.12.9/lib/getndelim2.c new file mode 100644 index 0000000000..91571ec83b --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getndelim2.c @@ -0,0 +1,114 @@ +/* getndelim2 - Read n characters or less from a stream, stopping at one of up + to two specified delimiters. + + Copyright (C) 1993, 1996, 1997, 1998, 2000, 2003 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Originally written by Jan Brittenson, bson@gnu.ai.mit.edu. */ + +#if HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "getndelim2.h" + +#include + +#include "unlocked-io.h" + +/* Always add at least this many bytes when extending the buffer. */ +#define MIN_CHUNK 64 + +ssize_t +getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t limit, + int delim1, int delim2, FILE *stream) +{ + size_t nbytes_avail; /* Allocated but unused chars in *LINEPTR. */ + char *read_pos; /* Where we're reading into *LINEPTR. */ + + if (!lineptr || !linesize || !stream) + return -1; + + if (!*lineptr) + { + *linesize = MIN_CHUNK; + *lineptr = malloc (*linesize); + if (!*lineptr) + return -1; + } + + if (*linesize < offset) + return -1; + + nbytes_avail = *linesize - offset; + read_pos = *lineptr + offset; + + for (;;) + { + /* Here always *lineptr + *linesize == read_pos + nbytes_avail. */ + register int c; + + if (limit == 0) + break; + + c = getc (stream); + + if (limit != GETNDELIM_NO_LIMIT) + limit--; + + /* We always want at least one char left in the buffer, since we + always (unless we get an error while reading the first char) + NUL-terminate the line buffer. */ + + if (nbytes_avail < 2) + { + if (*linesize > MIN_CHUNK) + *linesize *= 2; + else + *linesize += MIN_CHUNK; + + nbytes_avail = *linesize + *lineptr - read_pos; + *lineptr = realloc (*lineptr, *linesize); + if (!*lineptr) + return -1; + read_pos = *linesize - nbytes_avail + *lineptr; + } + + if (c == EOF) + { + /* Return partial line, if any. */ + if (read_pos == *lineptr) + return -1; + else + break; + } + + *read_pos++ = c; + nbytes_avail--; + + if (c == delim1 || (delim2 && c == delim2)) + /* Return the line. */ + break; + } + + /* Done - NUL terminate and return the number of chars read. + At this point we know that nbytes_avail >= 1. */ + *read_pos = '\0'; + + return read_pos - (*lineptr + offset); +} diff --git a/contrib/cvs-1.12.9/lib/getndelim2.h b/contrib/cvs-1.12.9/lib/getndelim2.h new file mode 100644 index 0000000000..b2426e37ba --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getndelim2.h @@ -0,0 +1,42 @@ +/* getndelim2 - Read n characters or less from a stream, stopping at one of up + to two specified delimiters. + + Copyright (C) 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef GETNDELIM2_H +#define GETNDELIM2_H 1 + +#include +#include + +/* Get ssize_t. */ +#include + +#define GETNDELIM_NO_LIMIT (ssize_t)-1 + +/* Read up to (and including) a delimiter DELIM1 from STREAM into *LINEPTR + + OFFSET (and NUL-terminate it). If DELIM2 is non-zero, then read up + and including the first occurrence of DELIM1 or DELIM2. *LINEPTR is + a pointer returned from malloc (or NULL), pointing to *LINESIZE bytes of + space. It is realloc'd as necessary. Read no more than LIMIT bytes. + Return the number of bytes read and stored at *LINEPTR + OFFSET (not + including the NUL terminator), or -1 on error or EOF. */ +extern ssize_t getndelim2 (char **_lineptr, size_t *_linesize, size_t _offset, + size_t _limit, int _delim1, int _delim2, + FILE *_stream); + +#endif /* GETNDELIM2_H */ diff --git a/contrib/cvs-1.12.9/lib/getnline.c b/contrib/cvs-1.12.9/lib/getnline.c new file mode 100644 index 0000000000..c16c99c193 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getnline.c @@ -0,0 +1,39 @@ +/* getnline - Read a line of n characters or less from a stream. + + Copyright (C) 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "getnline.h" + +#include "getndelim2.h" + +ssize_t +getnline (char **lineptr, size_t *linesize, size_t limit, FILE *stream) +{ + return getndelim2 (lineptr, linesize, 0, limit, '\n', 0, stream); +} + +ssize_t +getndelim (char **lineptr, size_t *linesize, size_t limit, int delimiter, + FILE *stream) +{ + return getndelim2 (lineptr, linesize, 0, limit, delimiter, 0, stream); +} diff --git a/contrib/cvs-1.12.9/lib/getnline.h b/contrib/cvs-1.12.9/lib/getnline.h new file mode 100644 index 0000000000..a879f871b0 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getnline.h @@ -0,0 +1,47 @@ +/* getnline - Read a line of n characters or less from a stream. + + Copyright (C) 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef GETNLINE_H +#define GETNLINE_H 1 + +#include +#include + +/* Get ssize_t. */ +#include + +/* Read a line, up to the next newline, from STREAM, and store it in *LINEPTR. + *LINEPTR is a pointer returned from malloc (or NULL), pointing to *LINESIZE + bytes of space. It is realloc'd as necessary. Read a maximum of LIMIT + bytes. + Return the number of bytes read and stored at *LINEPTR (not including the + NUL terminator), or -1 on error or EOF. */ +extern ssize_t getnline (char **_lineptr, size_t *_linesize, size_t limit, + FILE *_stream); + +/* Read a line, up to the next occurrence of DELIMITER, from STREAM, and store + it in *LINEPTR. + *LINEPTR is a pointer returned from malloc (or NULL), pointing to *LINESIZE + bytes of space. It is realloc'd as necessary. Read a maximum of LIMIT + bytes. + Return the number of bytes read and stored at *LINEPTR (not including the + NUL terminator), or -1 on error or EOF. */ +extern ssize_t getndelim (char **_lineptr, size_t *_linesize, size_t limit, + int _delimiter, FILE *_stream); + +#endif /* GETNLINE_H */ diff --git a/contrib/cvs-1.12.9/lib/getopt.c b/contrib/cvs-1.12.9/lib/getopt.c new file mode 100644 index 0000000000..6dcdbebf5e --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getopt.c @@ -0,0 +1,1209 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, + 1996, 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, + Inc. + + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#include + +#ifdef VMS +# include +#endif + +#ifdef _LIBC +# include +#else +/* This is for other GNU distributions with internationalized messages. */ +# include "gettext.h" +#endif +#define _(msgid) gettext (msgid) + +#if defined _LIBC && defined USE_IN_LIBIO +# include +#endif + +#ifndef attribute_hidden +# define attribute_hidden +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized attribute_hidden; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifndef __GNU_LIBRARY__ + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Stored original parameters. + XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv) +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (int argc, char *const *argv, const char *optstring) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (posixly_correct == NULL + && argc == __libc_argc && argv == __libc_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (int argc, char *const *argv, + const char *optstring, const struct option *longopts, + int *longind, int long_only) +{ + int print_errors = opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only + && (argv[optind][2] || !strchr (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]) >= 0) + { + + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); +#endif + } + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (argv[optind - 1][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#else + fprintf (stderr, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], + pfound->name); +#else + fprintf (stderr, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#endif + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]) >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); +#endif + } + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || strchr (optstring, *nextchar) == NULL) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (argv[optind][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); +#else + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); +#else + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#endif + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = strchr (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (posixly_correct) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: illegal option -- %c\n"), + argv[0], c); +#else + fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); +#endif + } + else + { +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: invalid option -- %c\n"), + argv[0], c); +#else + fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#endif + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, + _("%s: option requires an argument -- %c\n"), + argv[0], c) >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]) >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); +#endif + } + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name) >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#else + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]) >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); +#endif + } + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option requires an argument -- %c\n"), + argv[0], c) >= 0) + { + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/contrib/cvs-1.12.9/lib/getopt.h b/contrib/cvs-1.12.9/lib/getopt.h new file mode 100644 index 0000000000..1a55babfbc --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getopt.h @@ -0,0 +1,169 @@ +/* Declarations for getopt. + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, + 1999, 2001, 2003 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ + +#ifndef __need_getopt +extern int getopt_long (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +#endif + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/contrib/cvs-1.12.9/lib/getopt1.c b/contrib/cvs-1.12.9/lib/getopt1.c new file mode 100644 index 0000000000..3288c72613 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getopt1.c @@ -0,0 +1,185 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + + Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, + 1997, 1998, 2003 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef _LIBC +# include +#else +# include "getopt.h" +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (int argc, + char *const *argv, + const char *options, + const struct option *long_options, + int *opt_index) +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (int argc, + char *const *argv, + const char *options, + const struct option *long_options, + int *opt_index) +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + +# ifdef _LIBC +libc_hidden_def (getopt_long) +libc_hidden_def (getopt_long_only) +# endif + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/contrib/cvs-1.12.9/lib/getpass.c b/contrib/cvs-1.12.9/lib/getpass.c new file mode 100644 index 0000000000..d2668ec5e9 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/getpass.c @@ -0,0 +1,112 @@ +/* Copyright (C) 1992-2001, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include "getline.h" +#include "unlocked-io.h" + +/* It is desirable to use this bit on systems that have it. + The only bit of terminal state we want to twiddle is echoing, which is + done in software; there is no need to change the state of the terminal + hardware. */ + +#ifndef TCSASOFT +# define TCSASOFT 0 +#endif + +char * +getpass (const char *prompt) +{ + FILE *in, *out; + struct termios s, t; + int tty_changed; + static char *buf; + static size_t bufsize; + ssize_t nread; + + /* Try to write to and read from the terminal if we can. + If we can't open the terminal, use stderr and stdin. */ + + in = fopen ("/dev/tty", "w+"); + if (in == NULL) + { + in = stdin; + out = stderr; + } + else + out = in; + + /* Turn echoing off if it is on now. */ + + if (tcgetattr (fileno (in), &t) == 0) + { + /* Save the old one. */ + s = t; + /* Tricky, tricky. */ + t.c_lflag &= ~(ECHO|ISIG); + tty_changed = (tcsetattr (fileno (in), TCSAFLUSH|TCSASOFT, &t) == 0); + } + else + tty_changed = 0; + + /* Write the prompt. */ + fputs (prompt, out); + fflush (out); + + /* Read the password. */ + nread = getline (&buf, &bufsize, in); + if (buf != NULL) + { + if (nread < 0) + buf[0] = '\0'; + else if (buf[nread - 1] == '\n') + { + /* Remove the newline. */ + buf[nread - 1] = '\0'; + if (tty_changed) + { + /* Write the newline that was not echoed. + But before doing that, do a no-op fseek. According to the C + standard, input may not be followed by output on the same + stream without an intervening call to a file positioning + function. Without this fseek() call, on Solaris, HP-UX, + AIX, OSF/1, the previous input gets echoed, whereas on IRIX, + the following newline is not output as it should. */ + if (out == in) + fseek (out, 0, SEEK_CUR); + putc ('\n', out); + } + } + } + + /* Restore the original setting. */ + if (tty_changed) + (void) tcsetattr (fileno (in), TCSAFLUSH|TCSASOFT, &s); + + if (in != stdin) + /* We opened the terminal; now close it. */ + fclose (in); + + return buf; +} diff --git a/contrib/cvs-1.12.9/lib/gettext.h b/contrib/cvs-1.12.9/lib/gettext.h new file mode 100644 index 0000000000..8b262f4cfd --- /dev/null +++ b/contrib/cvs-1.12.9/lib/gettext.h @@ -0,0 +1,69 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, 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 + Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((const char *) (Msgid)) +# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +#endif /* _LIBGETTEXT_H */ diff --git a/contrib/cvs-1.12.9/lib/lstat.c b/contrib/cvs-1.12.9/lib/lstat.c new file mode 100644 index 0000000000..d0ad496f35 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/lstat.c @@ -0,0 +1,23 @@ +/* Work around the bug in some systems whereby lstat succeeds when + given the zero-length file name argument. The lstat from SunOS 4.1.4 + has this bug. + + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Free + Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define LSTAT +#include "stat.c" diff --git a/contrib/cvs-1.12.9/lib/md5.c b/contrib/cvs-1.12.9/lib/md5.c new file mode 100644 index 0000000000..d972e7ded6 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/md5.c @@ -0,0 +1,323 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * 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 + 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. */ + +/* Note regarding cvs_* namespace: this avoids potential conflicts + with libraries such as some versions of Kerberos. No particular + need to worry about whether the system supplies an MD5 library, as + this file is only about 3k of object code. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for memcpy() and memset() */ + +#include "md5.h" + +/* Little-endian byte-swapping routines. Note that these do not + depend on the size of datatypes such as cvs_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. */ + +static cvs_uint32 +getu32 (addr) + const unsigned char *addr; +{ + return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) + | addr[1]) << 8 | addr[0]; +} + +static void +putu32 (data, addr) + cvs_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); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +cvs_MD5Init (ctx) + struct cvs_MD5Context *ctx; +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +cvs_MD5Update (ctx, buf, len) + struct cvs_MD5Context *ctx; + unsigned char const *buf; + unsigned len; +{ + cvs_uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = (t + ((cvs_uint32)len << 3)) & 0xffffffff) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + cvs_MD5Transform (ctx->buf, ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + cvs_MD5Transform (ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +cvs_MD5Final (digest, ctx) + unsigned char digest[16]; + struct cvs_MD5Context *ctx; +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + cvs_MD5Transform (ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + + /* Append length in bits and transform */ + putu32(ctx->bits[0], ctx->in + 56); + putu32(ctx->bits[1], ctx->in + 60); + + cvs_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 */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* 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 &= 0xffffffff, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +cvs_MD5Transform (buf, inraw) + cvs_uint32 buf[4]; + const unsigned char inraw[64]; +{ + register cvs_uint32 a, b, c, d; + cvs_uint32 in[16]; + int i; + + for (i = 0; i < 16; ++i) + in[i] = getu32 (inraw + 4 * i); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif + +#ifdef TEST +/* Simple test program. Can use it to manually run the tests from + RFC1321 for example. */ +#include + +int +main (int argc, char **argv) +{ + struct cvs_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]); + cvs_MD5Init (&context); + cvs_MD5Update (&context, argv[j], strlen (argv[j])); + cvs_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-1.12.9/lib/md5.h b/contrib/cvs-1.12.9/lib/md5.h new file mode 100644 index 0000000000..2d3f0184b0 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/md5.h @@ -0,0 +1,26 @@ +/* See md5.c for explanation and copyright information. */ + +#ifndef MD5_H +#define MD5_H + +/* 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 cvs_uint32; + +struct cvs_MD5Context { + cvs_uint32 buf[4]; + cvs_uint32 bits[2]; + unsigned char in[64]; +}; + +void cvs_MD5Init (struct cvs_MD5Context *context); +void cvs_MD5Update (struct cvs_MD5Context *context, + unsigned char const *buf, unsigned len); +void cvs_MD5Final (unsigned char digest[16], + struct cvs_MD5Context *context); +void cvs_MD5Transform (cvs_uint32 buf[4], const unsigned char in[64]); + +#endif /* !MD5_H */ diff --git a/contrib/cvs-1.12.9/lib/pathmax.h b/contrib/cvs-1.12.9/lib/pathmax.h new file mode 100644 index 0000000000..bdd756e9c5 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/pathmax.h @@ -0,0 +1,54 @@ +/* Define PATH_MAX somehow. Requires sys/types.h. + Copyright (C) 1992, 1999, 2001, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _PATHMAX_H +# define _PATHMAX_H + +# if HAVE_UNISTD_H +# include +# endif + +/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define + PATH_MAX but might cause redefinition warnings when sys/param.h is + later included (as on MORE/BSD 4.3). */ +# if defined _POSIX_VERSION || !defined __GNUC__ +# include +# endif + +# ifndef _POSIX_PATH_MAX +# define _POSIX_PATH_MAX 255 +# endif + +# if !defined PATH_MAX && defined _PC_PATH_MAX +# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 \ + : pathconf ("/", _PC_PATH_MAX)) +# endif + +/* Don't include sys/param.h if it already has been. */ +# if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN +# include +# endif + +# if !defined PATH_MAX && defined MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# endif + +# ifndef PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# endif + +#endif /* _PATHMAX_H */ diff --git a/contrib/cvs-1.12.9/lib/printf-args.c b/contrib/cvs-1.12.9/lib/printf-args.c new file mode 100644 index 0000000000..926ae8ffe7 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/printf-args.c @@ -0,0 +1,118 @@ +/* Decomposed printf argument list. + Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "printf-args.h" + +#ifdef STATIC +STATIC +#endif +int +printf_fetchargs (va_list args, arguments *a) +{ + size_t i; + argument *ap; + + for (i = 0, ap = &a->arg[0]; i < a->count; i++, ap++) + switch (ap->type) + { + case TYPE_SCHAR: + ap->a.a_schar = va_arg (args, /*signed char*/ int); + break; + case TYPE_UCHAR: + ap->a.a_uchar = va_arg (args, /*unsigned char*/ int); + break; + case TYPE_SHORT: + ap->a.a_short = va_arg (args, /*short*/ int); + break; + case TYPE_USHORT: + ap->a.a_ushort = va_arg (args, /*unsigned short*/ int); + break; + case TYPE_INT: + ap->a.a_int = va_arg (args, int); + break; + case TYPE_UINT: + ap->a.a_uint = va_arg (args, unsigned int); + break; + case TYPE_LONGINT: + ap->a.a_longint = va_arg (args, long int); + break; + case TYPE_ULONGINT: + ap->a.a_ulongint = va_arg (args, unsigned long int); + break; +#ifdef HAVE_LONG_LONG + case TYPE_LONGLONGINT: + ap->a.a_longlongint = va_arg (args, long long int); + break; + case TYPE_ULONGLONGINT: + ap->a.a_ulonglongint = va_arg (args, unsigned long long int); + break; +#endif + case TYPE_DOUBLE: + ap->a.a_double = va_arg (args, double); + break; +#ifdef HAVE_LONG_DOUBLE + case TYPE_LONGDOUBLE: + ap->a.a_longdouble = va_arg (args, long double); + break; +#endif + case TYPE_CHAR: + ap->a.a_char = va_arg (args, int); + break; +#ifdef HAVE_WINT_T + case TYPE_WIDE_CHAR: + ap->a.a_wide_char = va_arg (args, wint_t); + break; +#endif + case TYPE_STRING: + ap->a.a_string = va_arg (args, const char *); + break; +#ifdef HAVE_WCHAR_T + case TYPE_WIDE_STRING: + ap->a.a_wide_string = va_arg (args, const wchar_t *); + break; +#endif + case TYPE_POINTER: + ap->a.a_pointer = va_arg (args, void *); + break; + case TYPE_COUNT_SCHAR_POINTER: + ap->a.a_count_schar_pointer = va_arg (args, signed char *); + break; + case TYPE_COUNT_SHORT_POINTER: + ap->a.a_count_short_pointer = va_arg (args, short *); + break; + case TYPE_COUNT_INT_POINTER: + ap->a.a_count_int_pointer = va_arg (args, int *); + break; + case TYPE_COUNT_LONGINT_POINTER: + ap->a.a_count_longint_pointer = va_arg (args, long int *); + break; +#ifdef HAVE_LONG_LONG + case TYPE_COUNT_LONGLONGINT_POINTER: + ap->a.a_count_longlongint_pointer = va_arg (args, long long int *); + break; +#endif + default: + /* Unknown type. */ + return -1; + } + return 0; +} diff --git a/contrib/cvs-1.12.9/lib/printf-args.h b/contrib/cvs-1.12.9/lib/printf-args.h new file mode 100644 index 0000000000..4b75c723f3 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/printf-args.h @@ -0,0 +1,136 @@ +/* Decomposed printf argument list. + Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _PRINTF_ARGS_H +#define _PRINTF_ARGS_H + +/* Get size_t. */ +#include + +/* Get wchar_t. */ +#ifdef HAVE_WCHAR_T +# include +#endif + +/* Get wint_t. */ +#ifdef HAVE_WINT_T +# include +#endif + +/* Get va_list. */ +#include + + +/* Argument types */ +typedef enum +{ + TYPE_NONE, + TYPE_SCHAR, + TYPE_UCHAR, + TYPE_SHORT, + TYPE_USHORT, + TYPE_INT, + TYPE_UINT, + TYPE_LONGINT, + TYPE_ULONGINT, +#ifdef HAVE_LONG_LONG + TYPE_LONGLONGINT, + TYPE_ULONGLONGINT, +#endif + TYPE_DOUBLE, +#ifdef HAVE_LONG_DOUBLE + TYPE_LONGDOUBLE, +#endif + TYPE_CHAR, +#ifdef HAVE_WINT_T + TYPE_WIDE_CHAR, +#endif + TYPE_STRING, +#ifdef HAVE_WCHAR_T + TYPE_WIDE_STRING, +#endif + TYPE_POINTER, + TYPE_COUNT_SCHAR_POINTER, + TYPE_COUNT_SHORT_POINTER, + TYPE_COUNT_INT_POINTER, + TYPE_COUNT_LONGINT_POINTER +#ifdef HAVE_LONG_LONG +, TYPE_COUNT_LONGLONGINT_POINTER +#endif +} arg_type; + +/* Polymorphic argument */ +typedef struct +{ + arg_type type; + union + { + signed char a_schar; + unsigned char a_uchar; + short a_short; + unsigned short a_ushort; + int a_int; + unsigned int a_uint; + long int a_longint; + unsigned long int a_ulongint; +#ifdef HAVE_LONG_LONG + long long int a_longlongint; + unsigned long long int a_ulonglongint; +#endif + float a_float; + double a_double; +#ifdef HAVE_LONG_DOUBLE + long double a_longdouble; +#endif + int a_char; +#ifdef HAVE_WINT_T + wint_t a_wide_char; +#endif + const char* a_string; +#ifdef HAVE_WCHAR_T + const wchar_t* a_wide_string; +#endif + void* a_pointer; + signed char * a_count_schar_pointer; + short * a_count_short_pointer; + int * a_count_int_pointer; + long int * a_count_longint_pointer; +#ifdef HAVE_LONG_LONG + long long int * a_count_longlongint_pointer; +#endif + } + a; +} +argument; + +typedef struct +{ + size_t count; + argument *arg; +} +arguments; + + +/* Fetch the arguments, putting them into a. */ +#ifdef STATIC +STATIC +#else +extern +#endif +int printf_fetchargs (va_list args, arguments *a); + +#endif /* _PRINTF_ARGS_H */ diff --git a/contrib/cvs-1.12.9/lib/printf-parse.c b/contrib/cvs-1.12.9/lib/printf-parse.c new file mode 100644 index 0000000000..b386b7b9b8 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/printf-parse.c @@ -0,0 +1,536 @@ +/* Formatted output to strings. + Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#if WIDE_CHAR_VERSION +# include "wprintf-parse.h" +#else +# include "printf-parse.h" +#endif + +/* Get size_t, NULL. */ +#include + +/* Get intmax_t. */ +#if HAVE_STDINT_H_WITH_UINTMAX +# include +#endif +#if HAVE_INTTYPES_H_WITH_UINTMAX +# include +#endif + +/* malloc(), realloc(), free(). */ +#include + +/* Checked size_t computations. */ +#include "xsize.h" + +#if WIDE_CHAR_VERSION +# define PRINTF_PARSE wprintf_parse +# define CHAR_T wchar_t +# define DIRECTIVE wchar_t_directive +# define DIRECTIVES wchar_t_directives +#else +# define PRINTF_PARSE printf_parse +# define CHAR_T char +# define DIRECTIVE char_directive +# define DIRECTIVES char_directives +#endif + +#ifdef STATIC +STATIC +#endif +int +PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a) +{ + const CHAR_T *cp = format; /* pointer into format */ + size_t arg_posn = 0; /* number of regular arguments consumed */ + size_t d_allocated; /* allocated elements of d->dir */ + size_t a_allocated; /* allocated elements of a->arg */ + size_t max_width_length = 0; + size_t max_precision_length = 0; + + d->count = 0; + d_allocated = 1; + d->dir = malloc (d_allocated * sizeof (DIRECTIVE)); + if (d->dir == NULL) + /* Out of memory. */ + return -1; + + a->count = 0; + a_allocated = 0; + a->arg = NULL; + +#define REGISTER_ARG(_index_,_type_) \ + { \ + size_t n = (_index_); \ + if (n >= a_allocated) \ + { \ + size_t memory_size; \ + argument *memory; \ + \ + a_allocated = xtimes (a_allocated, 2); \ + if (a_allocated <= n) \ + a_allocated = xsum (n, 1); \ + memory_size = xtimes (a_allocated, sizeof (argument)); \ + if (size_overflow_p (memory_size)) \ + /* Overflow, would lead to out of memory. */ \ + goto error; \ + memory = (a->arg \ + ? realloc (a->arg, memory_size) \ + : malloc (memory_size)); \ + if (memory == NULL) \ + /* Out of memory. */ \ + goto error; \ + a->arg = memory; \ + } \ + while (a->count <= n) \ + a->arg[a->count++].type = TYPE_NONE; \ + if (a->arg[n].type == TYPE_NONE) \ + a->arg[n].type = (_type_); \ + else if (a->arg[n].type != (_type_)) \ + /* Ambiguous type for positional argument. */ \ + goto error; \ + } + + while (*cp != '\0') + { + CHAR_T c = *cp++; + if (c == '%') + { + size_t arg_index = ARG_NONE; + DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */ + + /* Initialize the next directive. */ + dp->dir_start = cp - 1; + dp->flags = 0; + dp->width_start = NULL; + dp->width_end = NULL; + dp->width_arg_index = ARG_NONE; + dp->precision_start = NULL; + dp->precision_end = NULL; + dp->precision_arg_index = ARG_NONE; + dp->arg_index = ARG_NONE; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory later. */ + goto error; + arg_index = n - 1; + cp = np + 1; + } + } + + /* Read the flags. */ + for (;;) + { + if (*cp == '\'') + { + dp->flags |= FLAG_GROUP; + cp++; + } + else if (*cp == '-') + { + dp->flags |= FLAG_LEFT; + cp++; + } + else if (*cp == '+') + { + dp->flags |= FLAG_SHOWSIGN; + cp++; + } + else if (*cp == ' ') + { + dp->flags |= FLAG_SPACE; + cp++; + } + else if (*cp == '#') + { + dp->flags |= FLAG_ALT; + cp++; + } + else if (*cp == '0') + { + dp->flags |= FLAG_ZERO; + cp++; + } + else + break; + } + + /* Parse the field width. */ + if (*cp == '*') + { + dp->width_start = cp; + cp++; + dp->width_end = cp; + if (max_width_length < 1) + max_width_length = 1; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory later. */ + goto error; + dp->width_arg_index = n - 1; + cp = np + 1; + } + } + if (dp->width_arg_index == ARG_NONE) + { + dp->width_arg_index = arg_posn++; + if (dp->width_arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->width_arg_index, TYPE_INT); + } + else if (*cp >= '0' && *cp <= '9') + { + size_t width_length; + + dp->width_start = cp; + for (; *cp >= '0' && *cp <= '9'; cp++) + ; + dp->width_end = cp; + width_length = dp->width_end - dp->width_start; + if (max_width_length < width_length) + max_width_length = width_length; + } + + /* Parse the precision. */ + if (*cp == '.') + { + cp++; + if (*cp == '*') + { + dp->precision_start = cp - 1; + cp++; + dp->precision_end = cp; + if (max_precision_length < 2) + max_precision_length = 2; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory + later. */ + goto error; + dp->precision_arg_index = n - 1; + cp = np + 1; + } + } + if (dp->precision_arg_index == ARG_NONE) + { + dp->precision_arg_index = arg_posn++; + if (dp->precision_arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->precision_arg_index, TYPE_INT); + } + else + { + size_t precision_length; + + dp->precision_start = cp - 1; + for (; *cp >= '0' && *cp <= '9'; cp++) + ; + dp->precision_end = cp; + precision_length = dp->precision_end - dp->precision_start; + if (max_precision_length < precision_length) + max_precision_length = precision_length; + } + } + + { + arg_type type; + + /* Parse argument type/size specifiers. */ + { + int flags = 0; + + for (;;) + { + if (*cp == 'h') + { + flags |= (1 << (flags & 1)); + cp++; + } + else if (*cp == 'L') + { + flags |= 4; + cp++; + } + else if (*cp == 'l') + { + flags += 8; + cp++; + } +#ifdef HAVE_INTMAX_T + else if (*cp == 'j') + { + if (sizeof (intmax_t) > sizeof (long)) + { + /* intmax_t = long long */ + flags += 16; + } + else if (sizeof (intmax_t) > sizeof (int)) + { + /* intmax_t = long */ + flags += 8; + } + cp++; + } +#endif + else if (*cp == 'z' || *cp == 'Z') + { + /* 'z' is standardized in ISO C 99, but glibc uses 'Z' + because the warning facility in gcc-2.95.2 understands + only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ + if (sizeof (size_t) > sizeof (long)) + { + /* size_t = long long */ + flags += 16; + } + else if (sizeof (size_t) > sizeof (int)) + { + /* size_t = long */ + flags += 8; + } + cp++; + } + else if (*cp == 't') + { + if (sizeof (ptrdiff_t) > sizeof (long)) + { + /* ptrdiff_t = long long */ + flags += 16; + } + else if (sizeof (ptrdiff_t) > sizeof (int)) + { + /* ptrdiff_t = long */ + flags += 8; + } + cp++; + } + else + break; + } + + /* Read the conversion character. */ + c = *cp++; + switch (c) + { + case 'd': case 'i': +#ifdef HAVE_LONG_LONG + if (flags >= 16 || (flags & 4)) + type = TYPE_LONGLONGINT; + else +#endif + if (flags >= 8) + type = TYPE_LONGINT; + else if (flags & 2) + type = TYPE_SCHAR; + else if (flags & 1) + type = TYPE_SHORT; + else + type = TYPE_INT; + break; + case 'o': case 'u': case 'x': case 'X': +#ifdef HAVE_LONG_LONG + if (flags >= 16 || (flags & 4)) + type = TYPE_ULONGLONGINT; + else +#endif + if (flags >= 8) + type = TYPE_ULONGINT; + else if (flags & 2) + type = TYPE_UCHAR; + else if (flags & 1) + type = TYPE_USHORT; + else + type = TYPE_UINT; + break; + case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': + case 'a': case 'A': +#ifdef HAVE_LONG_DOUBLE + if (flags >= 16 || (flags & 4)) + type = TYPE_LONGDOUBLE; + else +#endif + type = TYPE_DOUBLE; + break; + case 'c': + if (flags >= 8) +#ifdef HAVE_WINT_T + type = TYPE_WIDE_CHAR; +#else + goto error; +#endif + else + type = TYPE_CHAR; + break; +#ifdef HAVE_WINT_T + case 'C': + type = TYPE_WIDE_CHAR; + c = 'c'; + break; +#endif + case 's': + if (flags >= 8) +#ifdef HAVE_WCHAR_T + type = TYPE_WIDE_STRING; +#else + goto error; +#endif + else + type = TYPE_STRING; + break; +#ifdef HAVE_WCHAR_T + case 'S': + type = TYPE_WIDE_STRING; + c = 's'; + break; +#endif + case 'p': + type = TYPE_POINTER; + break; + case 'n': +#ifdef HAVE_LONG_LONG + if (flags >= 16 || (flags & 4)) + type = TYPE_COUNT_LONGLONGINT_POINTER; + else +#endif + if (flags >= 8) + type = TYPE_COUNT_LONGINT_POINTER; + else if (flags & 2) + type = TYPE_COUNT_SCHAR_POINTER; + else if (flags & 1) + type = TYPE_COUNT_SHORT_POINTER; + else + type = TYPE_COUNT_INT_POINTER; + break; + case '%': + type = TYPE_NONE; + break; + default: + /* Unknown conversion character. */ + goto error; + } + } + + if (type != TYPE_NONE) + { + dp->arg_index = arg_index; + if (dp->arg_index == ARG_NONE) + { + dp->arg_index = arg_posn++; + if (dp->arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->arg_index, type); + } + dp->conversion = c; + dp->dir_end = cp; + } + + d->count++; + if (d->count >= d_allocated) + { + size_t memory_size; + DIRECTIVE *memory; + + d_allocated = xtimes (d_allocated, 2); + memory_size = xtimes (d_allocated, sizeof (DIRECTIVE)); + if (size_overflow_p (memory_size)) + /* Overflow, would lead to out of memory. */ + goto error; + memory = realloc (d->dir, memory_size); + if (memory == NULL) + /* Out of memory. */ + goto error; + d->dir = memory; + } + } + } + d->dir[d->count].dir_start = cp; + + d->max_width_length = max_width_length; + d->max_precision_length = max_precision_length; + return 0; + +error: + if (a->arg) + free (a->arg); + if (d->dir) + free (d->dir); + return -1; +} + +#undef DIRECTIVES +#undef DIRECTIVE +#undef CHAR_T +#undef PRINTF_PARSE diff --git a/contrib/cvs-1.12.9/lib/printf-parse.h b/contrib/cvs-1.12.9/lib/printf-parse.h new file mode 100644 index 0000000000..f3f4586606 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/printf-parse.h @@ -0,0 +1,74 @@ +/* Parse printf format string. + Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _PRINTF_PARSE_H +#define _PRINTF_PARSE_H + +#include "printf-args.h" + + +/* Flags */ +#define FLAG_GROUP 1 /* ' flag */ +#define FLAG_LEFT 2 /* - flag */ +#define FLAG_SHOWSIGN 4 /* + flag */ +#define FLAG_SPACE 8 /* space flag */ +#define FLAG_ALT 16 /* # flag */ +#define FLAG_ZERO 32 + +/* arg_index value indicating that no argument is consumed. */ +#define ARG_NONE (~(size_t)0) + +/* A parsed directive. */ +typedef struct +{ + const char* dir_start; + const char* dir_end; + int flags; + const char* width_start; + const char* width_end; + size_t width_arg_index; + const char* precision_start; + const char* precision_end; + size_t precision_arg_index; + char conversion; /* d i o u x X f e E g G c s p n U % but not C S */ + size_t arg_index; +} +char_directive; + +/* A parsed format string. */ +typedef struct +{ + size_t count; + char_directive *dir; + size_t max_width_length; + size_t max_precision_length; +} +char_directives; + + +/* Parses the format string. Fills in the number N of directives, and fills + in directives[0], ..., directives[N-1], and sets directives[N].dir_start + to the end of the format string. Also fills in the arg_type fields of the + arguments and the needed count of arguments. */ +#ifdef STATIC +STATIC +#else +extern +#endif +int printf_parse (const char *format, char_directives *d, arguments *a); + +#endif /* _PRINTF_PARSE_H */ diff --git a/contrib/cvs-1.12.9/lib/regex.h b/contrib/cvs-1.12.9/lib/regex.h new file mode 100644 index 0000000000..b4bbc01fd9 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/regex.h @@ -0,0 +1,556 @@ +/* Definitions for data structures and routines for the regular + expression library. + Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _REGEX_H +#define _REGEX_H 1 + +#include + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* POSIX says that must be included (by the caller) before + . */ + +#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS +/* VMS doesn't have `size_t' in , even though POSIX says it + should be there. */ +# include +#endif + +/* The following two types have to be signed and unsigned integer type + wide enough to hold a value of a pointer. For most ANSI compilers + ptrdiff_t and size_t should be likely OK. Still size of these two + types is 2 for Microsoft C. Ugh... */ +typedef long int s_reg_t; +typedef unsigned long int active_reg_t; + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned long int reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + +/* If this bit is set, do not process the GNU regex operators. + If not set, then the GNU regex operators are recognized. */ +#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) + +/* If this bit is set, turn on internal regex debugging. + If not set, and debugging was on, turn it off. + This only works if regex.c is compiled -DDEBUG. + We define this bit always, so that all that's needed to turn on + debugging is to recompile regex.c; the calling code can always have + this bit set, and it won't affect anything in the normal case. */ +#define RE_DEBUG (RE_NO_GNU_OPS << 1) + +/* If this bit is set, a syntactically invalid interval is treated as + a string of ordinary characters. For example, the ERE 'a{1' is + treated as 'a\{1'. */ +#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1) + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ + | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GNU_AWK \ + ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \ + & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \ + | RE_CONTEXT_INVALID_OPS )) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ + | RE_INTERVALS | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \ + | RE_INVALID_INTERVAL_ORD) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is + removed and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +/* Maximum number of duplicates an interval can allow. Some systems + (erroneously) define this in other header files, but we want our + value, so remove any previous define. */ +#ifdef RE_DUP_MAX +# undef RE_DUP_MAX +#endif +/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ +#define RE_DUP_MAX (0x7fff) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ +#ifdef _XOPEN_SOURCE + REG_ENOSYS = -1, /* This will never happen for this implementation. */ +#endif + + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Not implemented. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +#ifndef RE_TRANSLATE_TYPE +# define RE_TRANSLATE_TYPE char * +#endif + +struct re_pattern_buffer +{ +/* [[[begin pattern_buffer]]] */ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long int allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long int used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + RE_TRANSLATE_TYPE translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +# define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax (reg_syntax_t syntax); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern (const char *pattern, size_t length, + struct re_pattern_buffer *buffer); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap (struct re_pattern_buffer *buffer); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search (struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, + struct re_registers *regs); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 (struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, + int stop); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match (struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 (struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers (struct re_pattern_buffer *buffer, + struct re_registers *regs, unsigned num_regs, + regoff_t *starts, regoff_t *ends); + +#if defined _REGEX_RE_COMP || defined _LIBC +# ifndef _CRAY +/* 4.2 bsd compatibility. */ +extern char *re_comp (const char *); +extern int re_exec (const char *); +# endif +#endif + +/* GCC 2.95 and later have "__restrict"; C99 compilers have + "restrict", and "configure" may have defined "restrict". */ +#ifndef __restrict +# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) +# if defined restrict || 199901L <= __STDC_VERSION__ +# define __restrict restrict +# else +# define __restrict +# endif +# endif +#endif +/* gcc 3.1 and up support the [restrict] syntax. */ +#ifndef __restrict_arr +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define __restrict_arr __restrict +# else +# define __restrict_arr +# endif +#endif + +/* POSIX compatibility. */ +extern int regcomp (regex_t *__restrict __preg, + const char *__restrict __pattern, + int __cflags); + +extern int regexec (const regex_t *__restrict __preg, + const char *__restrict __string, size_t __nmatch, + regmatch_t __pmatch[__restrict_arr], + int __eflags); + +extern size_t regerror (int __errcode, const regex_t *__preg, + char *__errbuf, size_t __errbuf_size); + +extern void regfree (regex_t *__preg); + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* regex.h */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/contrib/cvs-1.12.9/lib/savecwd.c b/contrib/cvs-1.12.9/lib/savecwd.c new file mode 100644 index 0000000000..7a1764d85a --- /dev/null +++ b/contrib/cvs-1.12.9/lib/savecwd.c @@ -0,0 +1,142 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#ifdef STDC_HEADERS +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_FCNTL_H +# include +# include +#else +# include +#endif + +#ifdef HAVE_DIRECT_H +# include +#endif + +#ifdef HAVE_IO_H +# include +#endif + +#include +# ifndef errno +extern int errno; +#endif + +#include "savecwd.h" +#include "error.h" + +char *xgetwd( void ); + +/* Record the location of the current working directory in CWD so that + the program may change to other directories and later use restore_cwd + to return to the recorded location. This function may allocate + space using malloc (via xgetwd) or leave a file descriptor open; + use free_cwd to perform the necessary free or close. Upon failure, + no memory is allocated, any locally opened file descriptors are + closed; return non-zero -- in that case, free_cwd need not be + called, but doing so is ok. Otherwise, return zero. */ + +int +save_cwd (cwd) + struct saved_cwd *cwd; +{ + static int have_working_fchdir = 1; + + cwd->desc = -1; + cwd->name = NULL; + + if (have_working_fchdir) + { +#ifdef HAVE_FCHDIR + cwd->desc = open (".", O_RDONLY); + if (cwd->desc < 0) + { + error (0, errno, "cannot open current directory"); + return 1; + } + +# if __sun__ || sun + /* On SunOS 4, fchdir returns EINVAL if accounting is enabled, + so we have to fall back to chdir. */ + if (fchdir (cwd->desc)) + { + if (errno == EINVAL) + { + close (cwd->desc); + cwd->desc = -1; + have_working_fchdir = 0; + } + else + { + error (0, errno, "current directory"); + close (cwd->desc); + cwd->desc = -1; + return 1; + } + } +# endif /* __sun__ || sun */ +#else +#define fchdir(x) (abort (), 0) + have_working_fchdir = 0; +#endif + } + + if (!have_working_fchdir) + { + cwd->name = xgetwd (); + if (cwd->name == NULL) + { + error (0, errno, "cannot get current directory"); + return 1; + } + } + return 0; +} + +/* Change to recorded location, CWD, in directory hierarchy. + If "saved working directory", NULL)) + */ + +int +restore_cwd (cwd, dest) + const struct saved_cwd *cwd; + const char *dest; +{ + int fail = 0; + if (cwd->desc >= 0) + { + if (fchdir (cwd->desc)) + { + error (0, errno, "cannot return to %s", + (dest ? dest : "saved working directory")); + fail = 1; + } + } + else if (chdir (cwd->name) < 0) + { + error (0, errno, "%s", cwd->name); + fail = 1; + } + return fail; +} + +void +free_cwd (cwd) + struct saved_cwd *cwd; +{ + if (cwd->desc >= 0) + close (cwd->desc); + if (cwd->name) + free (cwd->name); +} + diff --git a/contrib/cvs-1.12.9/lib/savecwd.h b/contrib/cvs-1.12.9/lib/savecwd.h new file mode 100644 index 0000000000..165cc45b92 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/savecwd.h @@ -0,0 +1,14 @@ +#ifndef SAVE_CWD_H +#define SAVE_CWD_H 1 + +struct saved_cwd + { + int desc; + char *name; + }; + +int save_cwd (struct saved_cwd *cwd); +int restore_cwd (const struct saved_cwd *cwd, const char *dest); +void free_cwd (struct saved_cwd *cwd); + +#endif /* SAVE_CWD_H */ diff --git a/contrib/cvs-1.12.9/lib/sighandle.c b/contrib/cvs-1.12.9/lib/sighandle.c new file mode 100644 index 0000000000..7934461602 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/sighandle.c @@ -0,0 +1,416 @@ +/* sighandle.c -- Library routines for manipulating chains of signal handlers + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* Written by Paul Sander, HaL Computer Systems, Inc. + Brian Berliner added POSIX support */ + +/************************************************************************* + * + * signal.c -- This file contains code that manipulates chains of signal + * handlers. + * + * Facilities are provided to register a signal handler for + * any specific signal. When a signal is received, all of the + * registered signal handlers are invoked in the reverse order + * in which they are registered. Note that the signal handlers + * must not themselves make calls to the signal handling + * facilities. + * + *************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "system.h" + +#include +#include +#include + +#ifdef STDC_HEADERS +#include +#else +#if __STDC__ +char *calloc(unsigned nelem, unsigned size); +char *malloc(unsigned size); +#else +char *calloc(); +char *malloc(); +#endif /* __STDC__ */ +#endif /* STDC_HEADERS */ + +/* Define the highest signal number (usually) */ +#ifndef SIGMAX +#define SIGMAX 64 +#endif + +/* Define linked list of signal handlers structure */ +struct SIG_hlist { + RETSIGTYPE (*handler)(); + struct SIG_hlist *next; +}; + +/* + * Define array of lists of signal handlers. Note that this depends on + * the implementation to initialize each element to a null pointer. + */ + +static struct SIG_hlist **SIG_handlers; + +/* Define array of default signal vectors */ + +#ifdef POSIX_SIGNALS +static struct sigaction *SIG_defaults; +#else +#ifdef BSD_SIGNALS +static struct sigvec *SIG_defaults; +#else +static RETSIGTYPE (**SIG_defaults) (int); +#endif +#endif + +/* Critical section housekeeping */ +static int SIG_crSectNest = 0; /* Nesting level */ +#ifdef POSIX_SIGNALS +static sigset_t SIG_crSectMask; /* Signal mask */ +#else +static int SIG_crSectMask; /* Signal mask */ +#endif + +/* + * Initialize the signal handler arrays + */ + +static int SIG_init() +{ + int i; +#ifdef POSIX_SIGNALS + sigset_t sigset_test; +#endif + + if (SIG_defaults && SIG_handlers) /* already allocated */ + return (0); + +#ifdef POSIX_SIGNALS + (void) sigfillset(&sigset_test); + for (i = 1; i < SIGMAX && sigismember(&sigset_test, i) == 1; i++) + ; + if (i < SIGMAX) + i = SIGMAX; + i++; + if (!SIG_defaults) + SIG_defaults = (struct sigaction *) + calloc(i, sizeof(struct sigaction)); + (void) sigemptyset(&SIG_crSectMask); +#else + i = SIGMAX+1; +#ifdef BSD_SIGNALS + if (!SIG_defaults) + SIG_defaults = (struct sigvec *) + calloc(i, sizeof(struct sigvec)); +#else + if (!SIG_defaults) + SIG_defaults = ( RETSIGTYPE (**) (int) ) + calloc( i, sizeof( RETSIGTYPE (**) (int) ) ); +#endif + SIG_crSectMask = 0; +#endif + if (!SIG_handlers) + SIG_handlers = (struct SIG_hlist **) + calloc(i, sizeof(struct SIG_hlist *)); + return (!SIG_defaults || !SIG_handlers); +} + + + +/* + * The following begins a critical section. + */ +void SIG_beginCrSect (void) +{ + if (SIG_init() == 0) + { + if (SIG_crSectNest == 0) + { +#ifdef POSIX_SIGNALS + sigset_t sigset_mask; + + (void) sigfillset(&sigset_mask); + (void) sigprocmask(SIG_SETMASK, + &sigset_mask, &SIG_crSectMask); +#else +#ifdef BSD_SIGNALS + SIG_crSectMask = sigblock(~0); +#else + /* TBD */ +#endif +#endif + } + SIG_crSectNest++; + } +} + + + +/* + * The following ends a critical section. + */ +void SIG_endCrSect (void) +{ + if (SIG_init() == 0) + { + SIG_crSectNest--; + if (SIG_crSectNest == 0) + { +#ifdef POSIX_SIGNALS + (void) sigprocmask(SIG_SETMASK, &SIG_crSectMask, NULL); +#else +#ifdef BSD_SIGNALS + (void) sigsetmask(SIG_crSectMask); +#else + /* TBD */ +#endif +#endif + } + } +} + + + +/* + * The following invokes each signal handler in the reverse order in which + * they were registered. + */ +static RETSIGTYPE SIG_handle (int sig) +{ + struct SIG_hlist *this; + + /* Dispatch signal handlers */ + /* This crit section stuff is a CVSism - we know that our interrupt + * handlers will always end up exiting and we don't want them to be + * interrupted themselves. + */ + SIG_beginCrSect(); + this = SIG_handlers[sig]; + while (this != (struct SIG_hlist *) NULL) + { + (*this->handler)(sig); + this = this->next; + } + SIG_endCrSect(); + + return; +} + +/* + * The following registers a signal handler. If the handler is already + * registered, it is not registered twice, nor is the order in which signal + * handlers are invoked changed. If this is the first signal handler + * registered for a given signal, the old sigvec structure is saved for + * restoration later. + */ + +int SIG_register(int sig, RETSIGTYPE (*fn)()) +{ + int val; + struct SIG_hlist *this; +#ifdef POSIX_SIGNALS + struct sigaction act; + sigset_t sigset_mask, sigset_omask; +#else +#ifdef BSD_SIGNALS + struct sigvec vec; + int mask; +#endif +#endif + + /* Initialize */ + if (SIG_init() != 0) + return (-1); + val = 0; + + /* Block this signal while we look at handler chain */ +#ifdef POSIX_SIGNALS + (void) sigemptyset(&sigset_mask); + (void) sigaddset(&sigset_mask, sig); + (void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask); +#else +#ifdef BSD_SIGNALS + mask = sigblock(sigmask(sig)); +#endif +#endif + + /* See if this handler was already registered */ + this = SIG_handlers[sig]; + while (this != (struct SIG_hlist *) NULL) + { + if (this->handler == fn) break; + this = this->next; + } + + /* Register the new handler only if it is not already registered. */ + if (this == (struct SIG_hlist *) NULL) + { + + /* + * If this is the first handler registered for this signal, + * set up the signal handler dispatcher + */ + + if (SIG_handlers[sig] == (struct SIG_hlist *) NULL) + { +#ifdef POSIX_SIGNALS + act.sa_handler = SIG_handle; + (void) sigemptyset(&act.sa_mask); + act.sa_flags = 0; + val = sigaction(sig, &act, &SIG_defaults[sig]); +#else +#ifdef BSD_SIGNALS + memset (&vec, 0, sizeof (vec)); + vec.sv_handler = SIG_handle; + val = sigvec(sig, &vec, &SIG_defaults[sig]); +#else + if ((SIG_defaults[sig] = signal(sig, SIG_handle)) == SIG_ERR) + val = -1; +#endif +#endif + } + + /* If not, register it */ + if ((val == 0) && (this == (struct SIG_hlist *) NULL)) + { + this = (struct SIG_hlist *) + malloc(sizeof(struct SIG_hlist)); + if (this == NULL) + { + val = -1; + } + else + { + this->handler = fn; + this->next = SIG_handlers[sig]; + SIG_handlers[sig] = this; + } + } + } + + /* Unblock the signal */ +#ifdef POSIX_SIGNALS + (void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL); +#else +#ifdef BSD_SIGNALS + (void) sigsetmask(mask); +#endif +#endif + + return val; +} + + + +/* + * The following deregisters a signal handler. If the last signal handler for + * a given signal is deregistered, the default sigvec information is restored. + */ + +int SIG_deregister(int sig, RETSIGTYPE (*fn)()) +{ + int val; + struct SIG_hlist *this; + struct SIG_hlist *last; +#ifdef POSIX_SIGNALS + sigset_t sigset_mask, sigset_omask; +#else +#ifdef BSD_SIGNALS + int mask; +#endif +#endif + + /* Initialize */ + if (SIG_init() != 0) + return (-1); + val = 0; + last = (struct SIG_hlist *) NULL; + + /* Block this signal while we look at handler chain */ +#ifdef POSIX_SIGNALS + (void) sigemptyset(&sigset_mask); + (void) sigaddset(&sigset_mask, sig); + (void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask); +#else +#ifdef BSD_SIGNALS + mask = sigblock(sigmask(sig)); +#endif +#endif + + /* Search for the signal handler */ + this = SIG_handlers[sig]; + while ((this != (struct SIG_hlist *) NULL) && (this->handler != fn)) + { + last = this; + this = this->next; + } + + /* If it was registered, remove it */ + if (this != (struct SIG_hlist *) NULL) + { + if (last == (struct SIG_hlist *) NULL) + { + SIG_handlers[sig] = this->next; + } + else + { + last->next = this->next; + } + free((char *) this); + } + + /* Restore default behavior if there are no registered handlers */ + if (SIG_handlers[sig] == (struct SIG_hlist *) NULL) + { +#ifdef POSIX_SIGNALS + val = sigaction(sig, &SIG_defaults[sig], + (struct sigaction *) NULL); +#else +#ifdef BSD_SIGNALS + val = sigvec(sig, &SIG_defaults[sig], (struct sigvec *) NULL); +#else + if (signal(sig, SIG_defaults[sig]) == SIG_ERR) + val = -1; +#endif +#endif + } + + /* Unblock the signal */ +#ifdef POSIX_SIGNALS + (void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL); +#else +#ifdef BSD_SIGNALS + (void) sigsetmask(mask); +#endif +#endif + + return val; +} + + + +/* + * Return nonzero if currently in a critical section. + * Otherwise return zero. + */ + +int SIG_inCrSect (void) +{ + return SIG_crSectNest > 0; +} diff --git a/contrib/cvs-1.12.9/lib/stat.c b/contrib/cvs-1.12.9/lib/stat.c new file mode 100644 index 0000000000..069ebf8afa --- /dev/null +++ b/contrib/cvs-1.12.9/lib/stat.c @@ -0,0 +1,135 @@ +/* Work around the bug in some systems whereby stat/lstat succeeds when + given the zero-length file name argument. The stat/lstat from SunOS 4.1.4 + has this bug. Also work around a deficiency in Solaris systems (up to at + least Solaris 9) regarding the semantics of `lstat ("symlink/", sbuf).' + + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Free + Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* written by Jim Meyering */ + +#include + +#include +#include +#include +#ifndef errno +extern int errno; +#endif +#if defined LSTAT && ! LSTAT_FOLLOWS_SLASHED_SYMLINK +# include + +# if HAVE_STDLIB_H +# include +# endif + +# ifdef STAT_MACROS_BROKEN +# undef S_ISLNK +# endif + +# ifndef S_ISLNK +# ifdef S_IFLNK +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +# else +# define S_ISLNK(m) 0 +# endif +# endif + +# ifndef HAVE_DECL_FREE +"this configure-time declaration test was not run" +# endif +# if !HAVE_DECL_FREE +void free (); +# endif + +char *xmalloc (); + +/* lstat works differently on Linux and Solaris systems. POSIX (see + `pathname resolution' in the glossary) requires that programs like `ls' + take into consideration the fact that FILE has a trailing slash when + FILE is a symbolic link. On Linux systems, the lstat function already + has the desired semantics (in treating `lstat("symlink/",sbuf)' just like + `lstat("symlink/.",sbuf)', but on Solaris it does not. + + If FILE has a trailing slash and specifies a symbolic link, + then append a `.' to FILE and call lstat a second time. */ + +static int +slash_aware_lstat (const char *file, struct stat *sbuf) +{ + size_t len; + char *new_file; + + int lstat_result = lstat (file, sbuf); + + if (lstat_result != 0 || !S_ISLNK (sbuf->st_mode)) + return lstat_result; + + len = strlen (file); + if (file[len - 1] != '/') + return lstat_result; + + /* FILE refers to a symbolic link and the name ends with a slash. + Append a `.' to FILE and repeat the lstat call. */ + + /* Add one for the `.' we'll append, and one more for the trailing NUL. */ + new_file = xmalloc (len + 1 + 1); + memcpy (new_file, file, len); + new_file[len] = '.'; + new_file[len + 1] = 0; + + lstat_result = lstat (new_file, sbuf); + free (new_file); + + return lstat_result; +} +#endif /* LSTAT && ! LSTAT_FOLLOWS_SLASHED_SYMLINK */ + +/* This is a wrapper for stat/lstat. + If FILE is the empty string, fail with errno == ENOENT. + Otherwise, return the result of calling the real stat/lstat. + + This works around the bug in some systems whereby stat/lstat succeeds when + given the zero-length file name argument. The stat/lstat from SunOS 4.1.4 + has this bug. */ + +/* This function also provides a version of lstat with consistent semantics + when FILE specifies a symbolic link and has a trailing slash. */ + +#ifdef LSTAT +# define rpl_xstat rpl_lstat +# if ! LSTAT_FOLLOWS_SLASHED_SYMLINK +# define xstat_return_val(F, S) slash_aware_lstat (F, S) +# else +# define xstat_return_val(F, S) lstat (F, S) +# endif +#else +# define rpl_xstat rpl_stat +# define xstat_return_val(F, S) stat (F, S) +#endif + +int +rpl_xstat (const char *file, struct stat *sbuf) +{ + if (file && *file == 0) + { + errno = ENOENT; + return -1; + } + + return xstat_return_val (file, sbuf); +} diff --git a/contrib/cvs-1.12.9/lib/stripslash.c b/contrib/cvs-1.12.9/lib/stripslash.c new file mode 100644 index 0000000000..c6b319e69f --- /dev/null +++ b/contrib/cvs-1.12.9/lib/stripslash.c @@ -0,0 +1,39 @@ +/* stripslash.c -- remove redundant trailing slashes from a file name + Copyright (C) 1990, 2001, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "dirname.h" + +/* Remove trailing slashes from PATH. + Return nonzero if a trailing slash was removed. + This is useful when using filename completion from a shell that + adds a "/" after directory names (such as tcsh and bash), because + the Unix rename and rmdir system calls return an "Invalid argument" error + when given a path that ends in "/" (except for the root directory). */ + +int +strip_trailing_slashes (char *path) +{ + char *base = base_name (path); + char *base_lim = base + base_len (base); + int had_slash = *base_lim; + *base_lim = '\0'; + return had_slash; +} diff --git a/contrib/cvs-1.12.9/lib/system.h b/contrib/cvs-1.12.9/lib/system.h new file mode 100644 index 0000000000..e2a1b173a0 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/system.h @@ -0,0 +1,574 @@ +/* system-dependent definitions for CVS. + Copyright (C) 1989-1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/*** + *** Begin the default set of autoconf includes. + ***/ + +/* Headers assumed for C89 freestanding compilers. See HACKING for more. */ +#include +#include +#include + +/* C89 hosted headers assumed since they were included in UNIX version 7. + * See HACKING for more. + */ +#include +#include +#include +#include +#include + +/* C89 hosted headers we _think_ GCC supplies even on freestanding systems. + * If we find any systems which do not have them, a replacement header should + * be discussed with the GNULIB folks. + * + * For more information, please see the `Portability' section of the `HACKING' + * file. + */ +#include +#include + +/* We assume this because it has been around forever despite not being a part + * of any of the other standards we assume conformance to. So far this hasn't + * been a problem. + * + * For more information, please see the `Portability' section of the `HACKING' + * file. + */ +#include + +/* A GNULIB replacement for this C99 header is supplied when it is missing. + * See the comments in stdbool_.h for its limitations. + */ +#include + +/* Ditto for this POSIX.2 header. */ +#include + +/* For struct timespec. */ +#include "timespec.h" + + + +#if HAVE_SYS_STAT_H +# include +#endif /* HAVE_SYS_STAT_H */ +#if !STDC_HEADERS && HAVE_MEMORY_H +# include +#endif /* !STDC_HEADERS && HAVE_MEMORY_H */ +#if HAVE_INTTYPES_H +# include +#else /* ! HAVE_INTTYPES_H */ +# if HAVE_STDINT_H +# include +# endif /* HAVE_STDINT_H */ +#endif /* HAVE_INTTYPES_H */ +#if HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +/* End the default set of autoconf includes */ + +/* Assume these headers. */ +#include + +/* This include enables the use of the *_unlocked IO functions from glibc. */ +#include "unlocked-io.h" + +/* GNULIB includes */ +#include + +#ifdef STAT_MACROS_BROKEN +#undef S_ISBLK +#undef S_ISCHR +#undef S_ISDIR +#undef S_ISREG +#undef S_ISFIFO +#undef S_ISLNK +#undef S_ISSOCK +#undef S_ISMPB +#undef S_ISMPC +#undef S_ISNWK +#endif + +/* Not all systems have S_IFMT, but we want to use it if we have it. + The S_IFMT code below looks right (it masks and compares). The + non-S_IFMT code looks bogus (are there really systems on which + S_IFBLK, S_IFLNK, &c, each have their own bit? I suspect it was + written for OS/2 using the IBM C/C++ Tools 2.01 compiler). + + Of course POSIX systems will have S_IS*, so maybe the issue is + semi-moot. */ + +#if !defined(S_ISBLK) && defined(S_IFBLK) +# if defined(S_IFMT) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +# else +# define S_ISBLK(m) ((m) & S_IFBLK) +# endif +#endif + +#if !defined(S_ISCHR) && defined(S_IFCHR) +# if defined(S_IFMT) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +# else +# define S_ISCHR(m) ((m) & S_IFCHR) +# endif +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +# if defined(S_IFMT) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# else +# define S_ISDIR(m) ((m) & S_IFDIR) +# endif +#endif + +#if !defined(S_ISREG) && defined(S_IFREG) +# if defined(S_IFMT) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +# else +# define S_ISREG(m) ((m) & S_IFREG) +# endif +#endif + +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# if defined(S_IFMT) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +# else +# define S_ISFIFO(m) ((m) & S_IFIFO) +# endif +#endif + +#if !defined(S_ISLNK) && defined(S_IFLNK) +# if defined(S_IFMT) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +# else +# define S_ISLNK(m) ((m) & S_IFLNK) +# endif +#endif + +#ifndef S_ISSOCK +# if defined( S_IFSOCK ) +# ifdef S_IFMT +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +# else +# define S_ISSOCK(m) ((m) & S_IFSOCK) +# endif /* S_IFMT */ +# elif defined( S_ISNAM ) + /* SCO OpenServer 5.0.6a */ +# define S_ISSOCK S_ISNAM +# endif /* !S_IFSOCK && S_ISNAM */ +#endif /* !S_ISSOCK */ + +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +# if defined(S_IFMT) +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +# else +# define S_ISMPB(m) ((m) & S_IFMPB) +# define S_ISMPC(m) ((m) & S_IFMPC) +# endif +#endif + +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +# if defined(S_IFMT) +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +# else +# define S_ISNWK(m) ((m) & S_IFNWK) +# endif +#endif + +#ifdef NEED_DECOY_PERMISSIONS /* OS/2, really */ + +#define S_IRUSR S_IREAD +#define S_IWUSR S_IWRITE +#define S_IXUSR S_IEXEC +#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +#define S_IRGRP S_IREAD +#define S_IWGRP S_IWRITE +#define S_IXGRP S_IEXEC +#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) +#define S_IROTH S_IREAD +#define S_IWOTH S_IWRITE +#define S_IXOTH S_IEXEC +#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) + +#else /* ! NEED_DECOY_PERMISSIONS */ + +#ifndef S_IRUSR +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#define S_IXUSR 0100 +/* Read, write, and execute by owner. */ +#define S_IRWXU (S_IRUSR|S_IWUSR|S_IXUSR) + +#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */ +#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */ +#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */ +/* Read, write, and execute by group. */ +#define S_IRWXG (S_IRWXU >> 3) + +#define S_IROTH (S_IRGRP >> 3) /* Read by others. */ +#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */ +#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */ +/* Read, write, and execute by others. */ +#define S_IRWXO (S_IRWXG >> 3) +#endif /* !def S_IRUSR */ +#endif /* NEED_DECOY_PERMISSIONS */ + +#if defined(POSIX) || defined(HAVE_UNISTD_H) +# include +#else +off_t lseek (); +char *getcwd (); +#endif + +#ifdef HAVE_IO_H +#include +#endif + +#ifdef HAVE_DIRECT_H +#include +#endif + +/* The NeXT (without _POSIX_SOURCE, which we don't want) has a utime.h + which doesn't define anything. It would be cleaner to have configure + 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 +#else +# if defined (HAVE_SYS_UTIME_H) +# include +# else +# ifndef ALTOS +struct utimbuf +{ + long actime; + long modtime; +}; +# endif +int utime (); +# endif +#endif + +/* errno.h variations: + * + * Not all systems set the same error code on a non-existent-file + * error. This tries to ask the question somewhat portably. + * On systems that don't have ENOTEXIST, this should behave just like + * x == ENOENT. "x" is probably errno, of course. + */ +#ifdef ENOTEXIST +# ifdef EOS2ERR +# define existence_error(x) \ + (((x) == ENOTEXIST) || ((x) == ENOENT) || ((x) == EOS2ERR)) +# else +# define existence_error(x) \ + (((x) == ENOTEXIST) || ((x) == ENOENT)) +# endif +#else +# ifdef EVMSERR +# define existence_error(x) \ +((x) == ENOENT || (x) == EINVAL || (x) == EVMSERR) +# else +# define existence_error(x) ((x) == ENOENT) +# endif +#endif + +#ifdef HAVE_MALLOC +# define CVS_MALLOC malloc +# else /* !HAVE_MALLOC */ +# define CVS_MALLOC rpl_malloc +#endif /* HAVE_MALLOC */ +#ifdef HAVE_REALLOC +# define CVS_REALLOC realloc +#else /* !HAVE_REALLOC */ +# define CVS_REALLOC rpl_realloc +#endif /* HAVE_REALLOC */ + +#ifndef HAVE_STDLIB_H +char *getenv (); +char *malloc (); +char *realloc (); +char *calloc (); +extern int errno; +#endif + +/* check for POSIX signals */ +#if defined(HAVE_SIGACTION) && defined(HAVE_SIGPROCMASK) +# define POSIX_SIGNALS +#endif + +/* MINIX 1.6 doesn't properly support sigaction */ +#if defined(_MINIX) +# undef POSIX_SIGNALS +#endif + +/* If !POSIX, try for BSD.. Reason: 4.4BSD implements these as wrappers */ +#if !defined(POSIX_SIGNALS) +# if defined(HAVE_SIGVEC) && defined(HAVE_SIGSETMASK) && defined(HAVE_SIGBLOCK) +# define BSD_SIGNALS +# endif +#endif + +/* Under OS/2, this must be included _after_ stdio.h; that's why we do + it here. */ +#ifdef USE_OWN_TCPIP_H +# include "tcpip.h" +#endif + +#ifdef HAVE_FCNTL_H +# include +#else +# include +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 +# define SEEK_CUR 1 +# define SEEK_END 2 +#endif + +#ifndef F_OK +# define F_OK 0 +# define X_OK 1 +# define W_OK 2 +# define R_OK 4 +#endif + +#if HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +/* Convert B 512-byte blocks to kilobytes if K is nonzero, + otherwise return it unchanged. */ +#define convert_blocks(b, k) ((k) ? ((b) + 1) / 2 : (b)) + +#ifndef S_ISLNK +# define lstat stat +#endif + +/* + * Some UNIX distributions don't include these in their stat.h Defined here + * because "config.h" is always included last. + */ +#ifndef S_IWRITE +# define S_IWRITE 0000200 /* write permission, owner */ +#endif +#ifndef S_IWGRP +# define S_IWGRP 0000020 /* write permission, grougroup */ +#endif +#ifndef S_IWOTH +# define S_IWOTH 0000002 /* write permission, other */ +#endif + +/* 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_FDOPEN +# define CVS_FDOPEN fdopen +#endif + +#ifndef CVS_MKDIR +# define CVS_MKDIR mkdir +#endif + +#ifndef CVS_OPEN +# define CVS_OPEN open +#endif + +#ifndef CVS_READDIR +# define CVS_READDIR readdir +#endif + +#ifndef CVS_CLOSEDIR +# define CVS_CLOSEDIR closedir +#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 +/* Use the function from lib/stat.c when the system version is broken. + */ +# ifdef HAVE_STAT_EMPTY_STRING_BUG +# define CVS_STAT rpl_stat +# else /* !HAVE_STAT_EMPTY_STRING_BUG */ +# define CVS_STAT stat +# endif /* HAVE_STAT_EMPTY_STRING_BUG */ +#endif + +/* Open question: should CVS_STAT be lstat by default? We need + to use lstat in order to handle symbolic links correctly with + the PreservePermissions option. -twp */ +#ifndef CVS_LSTAT +/* Use the function from lib/lstat.c when the system version is broken. + */ +# if defined( HAVE_STAT_EMPTY_STRING_BUG ) || !defined( LSTAT_FOLLOWS_SLASHED_SYMLINK ) +# define CVS_LSTAT rpl_lstat +# else /* !defined(HAVE_STAT_EMPTY_STRING_BUG ) + * && defined( LSTAT_FOLLOWS_SLASHED_SYMLINK ) + */ +# define CVS_LSTAT lstat +# endif /* defined(HAVE_STAT_EMPTY_STRING_BUG ) + * || !defined( LSTAT_FOLLOWS_SLASHED_SYMLINK ) + */ +#endif + +#ifndef CVS_UNLINK +# define CVS_UNLINK unlink +#endif + +/* Wildcard matcher. Should be case-insensitive if the system is. */ +#ifndef CVS_FNMATCH +# define CVS_FNMATCH fnmatch +#endif + +#ifndef HAVE_FSEEKO +off_t ftello (FILE *); +int fseeko (FILE *, off_t, int); +#endif /* HAVE_FSEEKO */ + +#ifdef WIN32 +/* + * According to GNU conventions, we should avoid referencing any macro + * containing "WIN" as a reference to Microsoft Windows, as we would like to + * avoid any implication that we consider Microsoft Windows any sort of "win". + * + * FIXME: As of 2003-06-09, folks on the GNULIB project were discussing + * defining a configure macro to define WOE32 appropriately. If they ever do + * write such a beast, we should use it, though in most cases it would be + * preferable to avoid referencing any OS or compiler anyhow, per Autoconf + * convention, and reference only tested features of the system. + */ +# define WOE32 1 +#endif /* WIN32 */ + + + +#ifdef FILENAMES_CASE_INSENSITIVE + +# if defined (__CYGWIN32__) || defined (WOE32) + /* Under Windows, filenames are case-insensitive, and both / and \ + are path component separators. */ +# define FOLD_FN_CHAR(c) (WNT_filename_classes[(unsigned char) (c)]) +extern unsigned char WNT_filename_classes[]; +# else /* ! WOE32 */ + /* As far as I know, just Macintosh OS X can make it here, + * but since the OS X fold just folds a-z into A-Z or visa-versa, I'm just + * allowing it to be used for any case insensitive system which we aren't + * yet making other specific folds or exceptions for (basically, anything + * case insensitive other than Windows, where \ and C:\ style absolute paths + * also need to be accounted for). + * + * Under Mac OS X, filenames are case-insensitive. + */ +# define FOLD_FN_CHAR(c) (OSX_filename_classes[(unsigned char) (c)]) +extern unsigned char OSX_filename_classes[]; +# endif /* __CYGWIN32__ || WOE32 */ + +/* The following need to be declared for all case insensitive filesystems. + * When not FOLD_FN_CHAR is not #defined, a default definition for these + * functions is provided later in this header file. */ + +/* Like strcmp, but with the appropriate tweaks for file names. */ +extern int fncmp (const char *n1, const char *n2); + +/* Fold characters in FILENAME to their canonical forms. */ +extern void fnfold (char *FILENAME); + +#endif /* FILENAMES_CASE_INSENSITIVE */ + + + +/* 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 + to lower case. Under Windows NT, / and \ are both path component + separators, so FOLD_FN_CHAR would map them both to /. */ +#ifndef FOLD_FN_CHAR +# define FOLD_FN_CHAR(c) (c) +# define fnfold(filename) (filename) +# define fncmp strcmp +#endif + +/* Different file systems can have different naming patterns which designate + * a path as absolute. + */ +#ifndef ISABSOLUTE +# define ISABSOLUTE(s) ISSLASH(s[FILESYSTEM_PREFIX_LEN(s)]) +#endif + + +/* On some systems, we have to be careful about writing/reading files + in text or binary mode (so in text mode the system can handle CRLF + vs. LF, VMS text file conventions, &c). We decide to just always + be careful. That way we don't have to worry about whether text and + binary differ on this system. We just have to worry about whether + the system has O_BINARY and "rb". The latter is easy; all ANSI C + libraries have it, SunOS4 has it, and CVS has used it unguarded + some places for a while now without complaints (e.g. "rb" in + server.c (server_updated), since CVS 1.8). The former is just an + #ifdef. */ + +#define FOPEN_BINARY_READ ("rb") +#define FOPEN_BINARY_WRITE ("wb") +#define FOPEN_BINARY_READWRITE ("r+b") + +#ifdef O_BINARY +#define OPEN_BINARY (O_BINARY) +#else +#define OPEN_BINARY (0) +#endif diff --git a/contrib/cvs-1.12.9/lib/timespec.h b/contrib/cvs-1.12.9/lib/timespec.h new file mode 100644 index 0000000000..2c32a23db4 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/timespec.h @@ -0,0 +1,71 @@ +/* timespec -- System time interface + + Copyright (C) 2000, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if ! defined TIMESPEC_H +# define TIMESPEC_H + +/* You must include config.h before including this file. */ + +# include +# if TIME_WITH_SYS_TIME +# include +# include +# else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +# endif + +# if ! HAVE_STRUCT_TIMESPEC +/* Some systems don't define this struct, e.g., AIX 4.1, Ultrix 4.3. */ +struct timespec +{ + time_t tv_sec; + long tv_nsec; +}; +# endif + +# ifdef ST_MTIM_NSEC +# define ST_TIME_CMP_NS(a, b, ns) ((a).ns < (b).ns ? -1 : (a).ns > (b).ns) +# else +# define ST_TIME_CMP_NS(a, b, ns) 0 +# endif +# define ST_TIME_CMP(a, b, s, ns) \ + ((a).s < (b).s ? -1 : (a).s > (b).s ? 1 : ST_TIME_CMP_NS(a, b, ns)) +# define ATIME_CMP(a, b) ST_TIME_CMP (a, b, st_atime, st_atim.ST_MTIM_NSEC) +# define CTIME_CMP(a, b) ST_TIME_CMP (a, b, st_ctime, st_ctim.ST_MTIM_NSEC) +# define MTIME_CMP(a, b) ST_TIME_CMP (a, b, st_mtime, st_mtim.ST_MTIM_NSEC) + +# ifdef ST_MTIM_NSEC +# define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC) +# else +# define TIMESPEC_NS(timespec) 0 +# endif + +# if ! HAVE_DECL_NANOSLEEP +/* Don't specify a prototype here. Some systems (e.g., OSF) declare + nanosleep with a conflicting one (const-less first parameter). */ +int nanosleep (); +# endif + +int gettime (struct timespec *); +int settime (struct timespec const *); + +#endif diff --git a/contrib/cvs-1.12.9/lib/unlocked-io.h b/contrib/cvs-1.12.9/lib/unlocked-io.h new file mode 100644 index 0000000000..36a7a4885d --- /dev/null +++ b/contrib/cvs-1.12.9/lib/unlocked-io.h @@ -0,0 +1,132 @@ +/* Prefer faster, non-thread-safe stdio functions if available. + + Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Jim Meyering. */ + +#ifndef UNLOCKED_IO_H +# define UNLOCKED_IO_H 1 + +# ifndef USE_UNLOCKED_IO +# define USE_UNLOCKED_IO 1 +# endif + +# if USE_UNLOCKED_IO + +/* These are wrappers for functions/macros from the GNU C library, and + from other C libraries supporting POSIX's optional thread-safe functions. + + The standard I/O functions are thread-safe. These *_unlocked ones are + more efficient but not thread-safe. That they're not thread-safe is + fine since all of the applications in this package are single threaded. + + Also, some code that is shared with the GNU C library may invoke + the *_unlocked functions directly. On hosts that lack those + functions, invoke the non-thread-safe versions instead. */ + +# include + +# if HAVE_DECL_CLEARERR_UNLOCKED +# undef clearerr +# define clearerr(x) clearerr_unlocked (x) +# else +# define clearerr_unlocked(x) clearerr (x) +# endif +# if HAVE_DECL_FEOF_UNLOCKED +# undef feof +# define feof(x) feof_unlocked (x) +# else +# define feof_unlocked(x) feof (x) +# endif +# if HAVE_DECL_FERROR_UNLOCKED +# undef ferror +# define ferror(x) ferror_unlocked (x) +# else +# define ferror_unlocked(x) ferror (x) +# endif +# if HAVE_DECL_FFLUSH_UNLOCKED +# undef fflush +# define fflush(x) fflush_unlocked (x) +# else +# define fflush_unlocked(x) fflush (x) +# endif +# if HAVE_DECL_FGETS_UNLOCKED +# undef fgets +# define fgets(x,y,z) fgets_unlocked (x,y,z) +# else +# define fgets_unlocked(x,y,z) fgets (x,y,z) +# endif +# if HAVE_DECL_FPUTC_UNLOCKED +# undef fputc +# define fputc(x,y) fputc_unlocked (x,y) +# else +# define fputc_unlocked(x,y) fputc (x,y) +# endif +# if HAVE_DECL_FPUTS_UNLOCKED +# undef fputs +# define fputs(x,y) fputs_unlocked (x,y) +# else +# define fputs_unlocked(x,y) fputs (x,y) +# endif +# if HAVE_DECL_FREAD_UNLOCKED +# undef fread +# define fread(w,x,y,z) fread_unlocked (w,x,y,z) +# else +# define fread_unlocked(w,x,y,z) fread (w,x,y,z) +# endif +# if HAVE_DECL_FWRITE_UNLOCKED +# undef fwrite +# define fwrite(w,x,y,z) fwrite_unlocked (w,x,y,z) +# else +# define fwrite_unlocked(w,x,y,z) fwrite (w,x,y,z) +# endif +# if HAVE_DECL_GETC_UNLOCKED +# undef getc +# define getc(x) getc_unlocked (x) +# else +# define getc_unlocked(x) getc (x) +# endif +# if HAVE_DECL_GETCHAR_UNLOCKED +# undef getchar +# define getchar() getchar_unlocked () +# else +# define getchar_unlocked() getchar () +# endif +# if HAVE_DECL_PUTC_UNLOCKED +# undef putc +# define putc(x,y) putc_unlocked (x,y) +# else +# define putc_unlocked(x,y) putc (x,y) +# endif +# if HAVE_DECL_PUTCHAR_UNLOCKED +# undef putchar +# define putchar(x) putchar_unlocked (x) +# else +# define putchar_unlocked(x) putchar (x) +# endif + +# undef flockfile +# define flockfile(x) ((void) 0) + +# undef ftrylockfile +# define ftrylockfile(x) 0 + +# undef funlockfile +# define funlockfile(x) ((void) 0) + +# endif /* USE_UNLOCKED_IO */ +#endif /* UNLOCKED_IO_H */ diff --git a/contrib/cvs-1.12.9/lib/vasnprintf.c b/contrib/cvs-1.12.9/lib/vasnprintf.c new file mode 100644 index 0000000000..c0754ead9a --- /dev/null +++ b/contrib/cvs-1.12.9/lib/vasnprintf.c @@ -0,0 +1,886 @@ +/* vsprintf with automatic memory allocation. + Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Tell glibc's to provide a prototype for snprintf(). + This must come before because may include + , and once has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif +#ifndef IN_LIBINTL +# include +#endif + +/* Specification. */ +#if WIDE_CHAR_VERSION +# include "vasnwprintf.h" +#else +# include "vasnprintf.h" +#endif + +#include /* snprintf(), sprintf() */ +#include /* abort(), malloc(), realloc(), free() */ +#include /* memcpy(), strlen() */ +#include /* errno */ +#include /* CHAR_BIT */ +#include /* DBL_MAX_EXP, LDBL_MAX_EXP */ +#if WIDE_CHAR_VERSION +# include "wprintf-parse.h" +#else +# include "printf-parse.h" +#endif + +/* Checked size_t computations. */ +#include "xsize.h" + +#ifdef HAVE_WCHAR_T +# ifdef HAVE_WCSLEN +# define local_wcslen wcslen +# else + /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid + a dependency towards this library, here is a local substitute. + Define this substitute only once, even if this file is included + twice in the same compilation unit. */ +# ifndef local_wcslen_defined +# define local_wcslen_defined 1 +static size_t +local_wcslen (const wchar_t *s) +{ + const wchar_t *ptr; + + for (ptr = s; *ptr != (wchar_t) 0; ptr++) + ; + return ptr - s; +} +# endif +# endif +#endif + +#if WIDE_CHAR_VERSION +# define VASNPRINTF vasnwprintf +# define CHAR_T wchar_t +# define DIRECTIVE wchar_t_directive +# define DIRECTIVES wchar_t_directives +# define PRINTF_PARSE wprintf_parse +# define USE_SNPRINTF 1 +# if HAVE_DECL__SNWPRINTF + /* On Windows, the function swprintf() has a different signature than + on Unix; we use the _snwprintf() function instead. */ +# define SNPRINTF _snwprintf +# else + /* Unix. */ +# define SNPRINTF swprintf +# endif +#else +# define VASNPRINTF vasnprintf +# define CHAR_T char +# define DIRECTIVE char_directive +# define DIRECTIVES char_directives +# define PRINTF_PARSE printf_parse +# define USE_SNPRINTF (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) +# if HAVE_DECL__SNPRINTF + /* Windows. */ +# define SNPRINTF _snprintf +# else + /* Unix. */ +# define SNPRINTF snprintf +# endif +#endif + +CHAR_T * +VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list args) +{ + DIRECTIVES d; + arguments a; + + if (PRINTF_PARSE (format, &d, &a) < 0) + { + errno = EINVAL; + return NULL; + } + +#define CLEANUP() \ + free (d.dir); \ + if (a.arg) \ + free (a.arg); + + if (printf_fetchargs (args, &a) < 0) + { + CLEANUP (); + errno = EINVAL; + return NULL; + } + + { + size_t buf_neededlength; + CHAR_T *buf; + CHAR_T *buf_malloced; + const CHAR_T *cp; + size_t i; + DIRECTIVE *dp; + /* Output string accumulator. */ + CHAR_T *result; + size_t allocated; + size_t length; + + /* Allocate a small buffer that will hold a directive passed to + sprintf or snprintf. */ + buf_neededlength = + xsum4 (7, d.max_width_length, d.max_precision_length, 6); +#if HAVE_ALLOCA + if (buf_neededlength < 4000 / sizeof (CHAR_T)) + { + buf = (CHAR_T *) alloca (buf_neededlength * sizeof (CHAR_T)); + buf_malloced = NULL; + } + else +#endif + { + size_t buf_memsize = xtimes (buf_neededlength, sizeof (CHAR_T)); + if (size_overflow_p (buf_memsize)) + goto out_of_memory_1; + buf = (CHAR_T *) malloc (buf_memsize); + if (buf == NULL) + goto out_of_memory_1; + buf_malloced = buf; + } + + if (resultbuf != NULL) + { + result = resultbuf; + allocated = *lengthp; + } + else + { + result = NULL; + allocated = 0; + } + length = 0; + /* Invariants: + result is either == resultbuf or == NULL or malloc-allocated. + If length > 0, then result != NULL. */ + + /* Ensures that allocated >= needed. Aborts through a jump to + out_of_memory if needed is SIZE_MAX or otherwise too big. */ +#define ENSURE_ALLOCATION(needed) \ + if ((needed) > allocated) \ + { \ + size_t memory_size; \ + CHAR_T *memory; \ + \ + allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ + if ((needed) > allocated) \ + allocated = (needed); \ + memory_size = xtimes (allocated, sizeof (CHAR_T)); \ + if (size_overflow_p (memory_size)) \ + goto out_of_memory; \ + if (result == resultbuf || result == NULL) \ + memory = (CHAR_T *) malloc (memory_size); \ + else \ + memory = (CHAR_T *) realloc (result, memory_size); \ + if (memory == NULL) \ + goto out_of_memory; \ + if (result == resultbuf && length > 0) \ + memcpy (memory, result, length * sizeof (CHAR_T)); \ + result = memory; \ + } + + for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++) + { + if (cp != dp->dir_start) + { + size_t n = dp->dir_start - cp; + size_t augmented_length = xsum (length, n); + + ENSURE_ALLOCATION (augmented_length); + memcpy (result + length, cp, n * sizeof (CHAR_T)); + length = augmented_length; + } + if (i == d.count) + break; + + /* Execute a single directive. */ + if (dp->conversion == '%') + { + size_t augmented_length; + + if (!(dp->arg_index == ARG_NONE)) + abort (); + augmented_length = xsum (length, 1); + ENSURE_ALLOCATION (augmented_length); + result[length] = '%'; + length = augmented_length; + } + else + { + if (!(dp->arg_index != ARG_NONE)) + abort (); + + if (dp->conversion == 'n') + { + switch (a.arg[dp->arg_index].type) + { + case TYPE_COUNT_SCHAR_POINTER: + *a.arg[dp->arg_index].a.a_count_schar_pointer = length; + break; + case TYPE_COUNT_SHORT_POINTER: + *a.arg[dp->arg_index].a.a_count_short_pointer = length; + break; + case TYPE_COUNT_INT_POINTER: + *a.arg[dp->arg_index].a.a_count_int_pointer = length; + break; + case TYPE_COUNT_LONGINT_POINTER: + *a.arg[dp->arg_index].a.a_count_longint_pointer = length; + break; +#ifdef HAVE_LONG_LONG + case TYPE_COUNT_LONGLONGINT_POINTER: + *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; + break; +#endif + default: + abort (); + } + } + else + { + arg_type type = a.arg[dp->arg_index].type; + CHAR_T *p; + unsigned int prefix_count; + int prefixes[2]; +#if !USE_SNPRINTF + size_t tmp_length; + CHAR_T tmpbuf[700]; + CHAR_T *tmp; + + /* Allocate a temporary buffer of sufficient size for calling + sprintf. */ + { + size_t width; + size_t precision; + + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + width = (arg < 0 ? (unsigned int) (-arg) : arg); + } + else + { + const CHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + } + + precision = 6; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + precision = (arg < 0 ? 0 : arg); + } + else + { + const CHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + do + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); + while (digitp != dp->precision_end); + } + } + + switch (dp->conversion) + { + + case 'd': case 'i': case 'u': +# ifdef HAVE_LONG_LONG + if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long long) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + + 1 /* turn floor into ceil */ + + 1; /* account for leading sign */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + + 1 /* turn floor into ceil */ + + 1; /* account for leading sign */ + else + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + + 1 /* turn floor into ceil */ + + 1; /* account for leading sign */ + break; + + case 'o': +# ifdef HAVE_LONG_LONG + if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long long) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1 /* turn floor into ceil */ + + 1; /* account for leading sign */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1 /* turn floor into ceil */ + + 1; /* account for leading sign */ + else + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1 /* turn floor into ceil */ + + 1; /* account for leading sign */ + break; + + case 'x': case 'X': +# ifdef HAVE_LONG_LONG + if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long long) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1 /* turn floor into ceil */ + + 2; /* account for leading sign or alternate form */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1 /* turn floor into ceil */ + + 2; /* account for leading sign or alternate form */ + else + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1 /* turn floor into ceil */ + + 2; /* account for leading sign or alternate form */ + break; + + case 'f': case 'F': +# ifdef HAVE_LONG_DOUBLE + if (type == TYPE_LONGDOUBLE) + tmp_length = + (unsigned int) (LDBL_MAX_EXP + * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + + 1 /* turn floor into ceil */ + + 10; /* sign, decimal point etc. */ + else +# endif + tmp_length = + (unsigned int) (DBL_MAX_EXP + * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + + 1 /* turn floor into ceil */ + + 10; /* sign, decimal point etc. */ + tmp_length = xsum (tmp_length, precision); + break; + + case 'e': case 'E': case 'g': case 'G': + case 'a': case 'A': + tmp_length = + 12; /* sign, decimal point, exponent etc. */ + tmp_length = xsum (tmp_length, precision); + break; + + case 'c': +# if defined HAVE_WINT_T && !WIDE_CHAR_VERSION + if (type == TYPE_WIDE_CHAR) + tmp_length = MB_CUR_MAX; + else +# endif + tmp_length = 1; + break; + + case 's': +# ifdef HAVE_WCHAR_T + if (type == TYPE_WIDE_STRING) + { + tmp_length = + local_wcslen (a.arg[dp->arg_index].a.a_wide_string); + +# if !WIDE_CHAR_VERSION + tmp_length = xtimes (tmp_length, MB_CUR_MAX); +# endif + } + else +# endif + tmp_length = strlen (a.arg[dp->arg_index].a.a_string); + break; + + case 'p': + tmp_length = + (unsigned int) (sizeof (void *) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1 /* turn floor into ceil */ + + 2; /* account for leading 0x */ + break; + + default: + abort (); + } + + if (tmp_length < width) + tmp_length = width; + + tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ + } + + if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T)) + tmp = tmpbuf; + else + { + size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (CHAR_T *) malloc (tmp_memsize); + if (tmp == NULL) + /* Out of memory. */ + goto out_of_memory; + } +#endif + + /* Construct the format string for calling snprintf or + sprintf. */ + p = buf; + *p++ = '%'; + if (dp->flags & FLAG_GROUP) + *p++ = '\''; + if (dp->flags & FLAG_LEFT) + *p++ = '-'; + if (dp->flags & FLAG_SHOWSIGN) + *p++ = '+'; + if (dp->flags & FLAG_SPACE) + *p++ = ' '; + if (dp->flags & FLAG_ALT) + *p++ = '#'; + if (dp->flags & FLAG_ZERO) + *p++ = '0'; + if (dp->width_start != dp->width_end) + { + size_t n = dp->width_end - dp->width_start; + memcpy (p, dp->width_start, n * sizeof (CHAR_T)); + p += n; + } + if (dp->precision_start != dp->precision_end) + { + size_t n = dp->precision_end - dp->precision_start; + memcpy (p, dp->precision_start, n * sizeof (CHAR_T)); + p += n; + } + + switch (type) + { +#ifdef HAVE_LONG_LONG + case TYPE_LONGLONGINT: + case TYPE_ULONGLONGINT: + *p++ = 'l'; + /*FALLTHROUGH*/ +#endif + case TYPE_LONGINT: + case TYPE_ULONGINT: +#ifdef HAVE_WINT_T + case TYPE_WIDE_CHAR: +#endif +#ifdef HAVE_WCHAR_T + case TYPE_WIDE_STRING: +#endif + *p++ = 'l'; + break; +#ifdef HAVE_LONG_DOUBLE + case TYPE_LONGDOUBLE: + *p++ = 'L'; + break; +#endif + default: + break; + } + *p = dp->conversion; +#if USE_SNPRINTF + p[1] = '%'; + p[2] = 'n'; + p[3] = '\0'; +#else + p[1] = '\0'; +#endif + + /* Construct the arguments for calling snprintf or sprintf. */ + prefix_count = 0; + if (dp->width_arg_index != ARG_NONE) + { + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; + } + if (dp->precision_arg_index != ARG_NONE) + { + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int; + } + +#if USE_SNPRINTF + /* Prepare checking whether snprintf returns the count + via %n. */ + ENSURE_ALLOCATION (xsum (length, 1)); + result[length] = '\0'; +#endif + + for (;;) + { + size_t maxlen; + int count; + int retcount; + + maxlen = allocated - length; + count = -1; + retcount = 0; + +#if USE_SNPRINTF +# define SNPRINTF_BUF(arg) \ + switch (prefix_count) \ + { \ + case 0: \ + retcount = SNPRINTF (result + length, maxlen, buf, \ + arg, &count); \ + break; \ + case 1: \ + retcount = SNPRINTF (result + length, maxlen, buf, \ + prefixes[0], arg, &count); \ + break; \ + case 2: \ + retcount = SNPRINTF (result + length, maxlen, buf, \ + prefixes[0], prefixes[1], arg, \ + &count); \ + break; \ + default: \ + abort (); \ + } +#else +# define SNPRINTF_BUF(arg) \ + switch (prefix_count) \ + { \ + case 0: \ + count = sprintf (tmp, buf, arg); \ + break; \ + case 1: \ + count = sprintf (tmp, buf, prefixes[0], arg); \ + break; \ + case 2: \ + count = sprintf (tmp, buf, prefixes[0], prefixes[1],\ + arg); \ + break; \ + default: \ + abort (); \ + } +#endif + + switch (type) + { + case TYPE_SCHAR: + { + int arg = a.arg[dp->arg_index].a.a_schar; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UCHAR: + { + unsigned int arg = a.arg[dp->arg_index].a.a_uchar; + SNPRINTF_BUF (arg); + } + break; + case TYPE_SHORT: + { + int arg = a.arg[dp->arg_index].a.a_short; + SNPRINTF_BUF (arg); + } + break; + case TYPE_USHORT: + { + unsigned int arg = a.arg[dp->arg_index].a.a_ushort; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT: + { + int arg = a.arg[dp->arg_index].a.a_int; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT: + { + unsigned int arg = a.arg[dp->arg_index].a.a_uint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_LONGINT: + { + long int arg = a.arg[dp->arg_index].a.a_longint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_ULONGINT: + { + unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint; + SNPRINTF_BUF (arg); + } + break; +#ifdef HAVE_LONG_LONG + case TYPE_LONGLONGINT: + { + long long int arg = a.arg[dp->arg_index].a.a_longlongint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_ULONGLONGINT: + { + unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_DOUBLE: + { + double arg = a.arg[dp->arg_index].a.a_double; + SNPRINTF_BUF (arg); + } + break; +#ifdef HAVE_LONG_DOUBLE + case TYPE_LONGDOUBLE: + { + long double arg = a.arg[dp->arg_index].a.a_longdouble; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_CHAR: + { + int arg = a.arg[dp->arg_index].a.a_char; + SNPRINTF_BUF (arg); + } + break; +#ifdef HAVE_WINT_T + case TYPE_WIDE_CHAR: + { + wint_t arg = a.arg[dp->arg_index].a.a_wide_char; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_STRING: + { + const char *arg = a.arg[dp->arg_index].a.a_string; + SNPRINTF_BUF (arg); + } + break; +#ifdef HAVE_WCHAR_T + case TYPE_WIDE_STRING: + { + const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_POINTER: + { + void *arg = a.arg[dp->arg_index].a.a_pointer; + SNPRINTF_BUF (arg); + } + break; + default: + abort (); + } + +#if USE_SNPRINTF + /* Portability: Not all implementations of snprintf() + are ISO C 99 compliant. Determine the number of + bytes that snprintf() has produced or would have + produced. */ + if (count >= 0) + { + /* Verify that snprintf() has NUL-terminated its + result. */ + if (count < maxlen && result[length + count] != '\0') + abort (); + /* Portability hack. */ + if (retcount > count) + count = retcount; + } + else + { + /* snprintf() doesn't understand the '%n' + directive. */ + if (p[1] != '\0') + { + /* Don't use the '%n' directive; instead, look + at the snprintf() return value. */ + p[1] = '\0'; + continue; + } + else + { + /* Look at the snprintf() return value. */ + if (retcount < 0) + { + /* HP-UX 10.20 snprintf() is doubly deficient: + It doesn't understand the '%n' directive, + *and* it returns -1 (rather than the length + that would have been required) when the + buffer is too small. */ + size_t bigger_need = + xsum (xtimes (allocated, 2), 12); + ENSURE_ALLOCATION (bigger_need); + continue; + } + else + count = retcount; + } + } +#endif + + /* Attempt to handle failure. */ + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EINVAL; + return NULL; + } + +#if !USE_SNPRINTF + if (count >= tmp_length) + /* tmp_length was incorrectly calculated - fix the + code above! */ + abort (); +#endif + + /* Make room for the result. */ + if (count >= maxlen) + { + /* Need at least count bytes. But allocate + proportionally, to avoid looping eternally if + snprintf() reports a too small count. */ + size_t n = + xmax (xsum (length, count), xtimes (allocated, 2)); + + ENSURE_ALLOCATION (n); +#if USE_SNPRINTF + continue; +#endif + } + +#if USE_SNPRINTF + /* The snprintf() result did fit. */ +#else + /* Append the sprintf() result. */ + memcpy (result + length, tmp, count * sizeof (CHAR_T)); + if (tmp != tmpbuf) + free (tmp); +#endif + + length += count; + break; + } + } + } + } + + /* Add the final NUL. */ + ENSURE_ALLOCATION (xsum (length, 1)); + result[length] = '\0'; + + if (result != resultbuf && length + 1 < allocated) + { + /* Shrink the allocated memory if possible. */ + CHAR_T *memory; + + memory = (CHAR_T *) realloc (result, (length + 1) * sizeof (CHAR_T)); + if (memory != NULL) + result = memory; + } + + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + *lengthp = length; + return result; + + out_of_memory: + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + out_of_memory_1: + CLEANUP (); + errno = ENOMEM; + return NULL; + } +} + +#undef SNPRINTF +#undef USE_SNPRINTF +#undef PRINTF_PARSE +#undef DIRECTIVES +#undef DIRECTIVE +#undef CHAR_T +#undef VASNPRINTF diff --git a/contrib/cvs-1.12.9/lib/vasnprintf.h b/contrib/cvs-1.12.9/lib/vasnprintf.h new file mode 100644 index 0000000000..4edb95d654 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/vasnprintf.h @@ -0,0 +1,60 @@ +/* vsprintf with automatic memory allocation. + Copyright (C) 2002-2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _VASNPRINTF_H +#define _VASNPRINTF_H + +/* Get va_list. */ +#include + +/* Get size_t. */ +#include + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __format__ format +# define __printf__ printf +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Write formatted output to a string dynamically allocated with malloc(). + You can pass a preallocated buffer for the result in RESULTBUF and its + size in *LENGTHP; otherwise you pass RESULTBUF = NULL. + If successful, return the address of the string (this may be = RESULTBUF + if no dynamic memory allocation was necessary) and set *LENGTHP to the + number of resulting bytes, excluding the trailing NUL. Upon error, set + errno and return NULL. */ +extern char * asnprintf (char *resultbuf, size_t *lengthp, const char *format, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); +extern char * vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) + __attribute__ ((__format__ (__printf__, 3, 0))); + +#ifdef __cplusplus +} +#endif + +#endif /* _VASNPRINTF_H */ diff --git a/contrib/cvs-1.12.9/lib/wait.h b/contrib/cvs-1.12.9/lib/wait.h new file mode 100644 index 0000000000..81df938505 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/wait.h @@ -0,0 +1,42 @@ +/* wait.h -- POSIX macros for evaluating exit statuses + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +#ifdef HAVE_SYS_WAIT_H +#include /* For pid_t. */ +#ifdef HAVE_SYS_RESOURCE_H +#include /* for rusage */ +#endif +#include +#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 WCOREDUMP /* not POSIX, but common and useful */ +#define WCOREDUMP(w) (((w) & 0x80) != 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-1.12.9/lib/xalloc.h b/contrib/cvs-1.12.9/lib/xalloc.h new file mode 100644 index 0000000000..4b6585811b --- /dev/null +++ b/contrib/cvs-1.12.9/lib/xalloc.h @@ -0,0 +1,87 @@ +/* xalloc.h -- malloc with out-of-memory checking + + Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef XALLOC_H_ +# define XALLOC_H_ + +# include + +# ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ +# define __attribute__(x) +# endif +# endif + +# ifndef ATTRIBUTE_NORETURN +# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) +# endif + +/* If this pointer is non-zero, run the specified function upon each + allocation failure. It is initialized to zero. */ +extern void (*xalloc_fail_func) (void); + +/* If XALLOC_FAIL_FUNC is undefined or a function that returns, this + message is output. It is translated via gettext. + Its value is "memory exhausted". */ +extern char const xalloc_msg_memory_exhausted[]; + +/* This function is always triggered when memory is exhausted. It is + in charge of honoring the two previous items. It exits with status + exit_failure (defined in exitfail.h). This is the + function to call when one wants the program to die because of a + memory allocation failure. */ +extern void xalloc_die (void) ATTRIBUTE_NORETURN; + +void *xmalloc (size_t s); +void *xnmalloc (size_t n, size_t s); +void *xzalloc (size_t s); +void *xcalloc (size_t n, size_t s); +void *xrealloc (void *p, size_t s); +void *xnrealloc (void *p, size_t n, size_t s); +void *x2realloc (void *p, size_t *pn); +void *x2nrealloc (void *p, size_t *pn, size_t s); +void *xclone (void const *p, size_t s); +char *xstrdup (const char *str); + +/* Return 1 if an array of N objects, each of size S, cannot exist due + to size arithmetic overflow. S must be positive and N must be + nonnegative. This is a macro, not an inline function, so that it + works correctly even when SIZE_MAX < N. + + By gnulib convention, SIZE_MAX represents overflow in size + calculations, so the conservative dividend to use here is + SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value. + However, malloc (SIZE_MAX) fails on all known hosts where + sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for + exactly-SIZE_MAX allocations on such hosts; this avoids a test and + branch when S is known to be 1. */ +# define xalloc_oversized(n, s) \ + ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n)) + +/* These macros are deprecated; they will go away soon, and are retained + temporarily only to ease conversion to the functions described above. */ +# define CCLONE(p, n) xclone (p, (n) * sizeof *(p)) +# define CLONE(p) xclone (p, sizeof *(p)) +# define NEW(type, var) type *var = xmalloc (sizeof (type)) +# define XCALLOC(type, n) xcalloc (n, sizeof (type)) +# define XMALLOC(type, n) xnmalloc (n, sizeof (type)) +# define XREALLOC(p, type, n) xnrealloc (p, n, sizeof (type)) +# define XFREE(p) free (p) + +#endif /* !XALLOC_H_ */ diff --git a/contrib/cvs-1.12.9/lib/xgetwd.c b/contrib/cvs-1.12.9/lib/xgetwd.c new file mode 100644 index 0000000000..8cea4d9c7f --- /dev/null +++ b/contrib/cvs-1.12.9/lib/xgetwd.c @@ -0,0 +1,68 @@ +/* xgetwd.c -- return current directory with unlimited length + Copyright (C) 1992, 1997 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* Derived from xgetcwd.c in e.g. the GNU sh-utils. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "system.h" +#include "pathmax.h" + +#include +#include +#ifndef errno +extern int errno; +#endif +#include + +/* Amount by which to increase buffer size when allocating more space. */ +#define PATH_INCR 32 + +char *xmalloc (); +char *xrealloc (); + +/* Return the current directory, newly allocated, arbitrarily long. + Return NULL and set errno on error. */ + +char * +xgetwd () +{ + char *cwd; + char *ret; + unsigned path_max; + + errno = 0; + path_max = (unsigned) PATH_MAX; + path_max += 2; /* The getcwd docs say to do this. */ + + cwd = xmalloc (path_max); + + errno = 0; + while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) + { + path_max += PATH_INCR; + cwd = xrealloc (cwd, path_max); + errno = 0; + } + + if (ret == NULL) + { + int save_errno = errno; + free (cwd); + errno = save_errno; + return NULL; + } + return cwd; +} diff --git a/contrib/cvs-1.12.9/lib/xmalloc.c b/contrib/cvs-1.12.9/lib/xmalloc.c new file mode 100644 index 0000000000..181006b43d --- /dev/null +++ b/contrib/cvs-1.12.9/lib/xmalloc.c @@ -0,0 +1,255 @@ +/* xmalloc.c -- malloc with out of memory checking + + Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2003, + 1999, 2000, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "xalloc.h" + +#include +#include + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +#include "error.h" +#include "exitfail.h" + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +#ifndef HAVE_MALLOC +"you must run the autoconf test for a GNU libc compatible malloc" +#endif + +#ifndef HAVE_REALLOC +"you must run the autoconf test for a GNU libc compatible realloc" +#endif + +/* If non NULL, call this function when memory is exhausted. */ +void (*xalloc_fail_func) (void) = 0; + +/* If XALLOC_FAIL_FUNC is NULL, or does return, display this message + before exiting when memory is exhausted. Goes through gettext. */ +char const xalloc_msg_memory_exhausted[] = N_("memory exhausted"); + +void +xalloc_die (void) +{ + if (xalloc_fail_func) + (*xalloc_fail_func) (); + error (exit_failure, 0, "%s", _(xalloc_msg_memory_exhausted)); + /* The `noreturn' cannot be given to error, since it may return if + its first argument is 0. To help compilers understand the + xalloc_die does terminate, call abort. */ + abort (); +} + +/* Allocate an array of N objects, each with S bytes of memory, + dynamically, with error checking. S must be nonzero. */ + +static inline void * +xnmalloc_inline (size_t n, size_t s) +{ + void *p; + if (xalloc_oversized (n, s) || ! (p = malloc (n * s))) + xalloc_die (); + return p; +} + +void * +xnmalloc (size_t n, size_t s) +{ + return xnmalloc_inline (n, s); +} + +/* Allocate N bytes of memory dynamically, with error checking. */ + +void * +xmalloc (size_t n) +{ + return xnmalloc_inline (n, 1); +} + +/* Change the size of an allocated block of memory P to an array of N + objects each of S bytes, with error checking. S must be nonzero. */ + +static inline void * +xnrealloc_inline (void *p, size_t n, size_t s) +{ + if (xalloc_oversized (n, s) || ! (p = realloc (p, n * s))) + xalloc_die (); + return p; +} + +void * +xnrealloc (void *p, size_t n, size_t s) +{ + return xnrealloc_inline (p, n, s); +} + +/* Change the size of an allocated block of memory P to N bytes, + with error checking. */ + +void * +xrealloc (void *p, size_t n) +{ + return xnrealloc_inline (p, n, 1); +} + + +/* If P is null, allocate a block of at least *PN such objects; + otherwise, reallocate P so that it contains more than *PN objects + each of S bytes. *PN must be nonzero unless P is null, and S must + be nonzero. Set *PN to the new number of objects, and return the + pointer to the new block. *PN is never set to zero, and the + returned pointer is never null. + + Repeated reallocations are guaranteed to make progress, either by + allocating an initial block with a nonzero size, or by allocating a + larger block. + + In the following implementation, nonzero sizes are doubled so that + repeated reallocations have O(N log N) overall cost rather than + O(N**2) cost, but the specification for this function does not + guarantee that sizes are doubled. + + Here is an example of use: + + int *p = NULL; + size_t used = 0; + size_t allocated = 0; + + void + append_int (int value) + { + if (used == allocated) + p = x2nrealloc (p, &allocated, sizeof *p); + p[used++] = value; + } + + This causes x2nrealloc to allocate a block of some nonzero size the + first time it is called. + + To have finer-grained control over the initial size, set *PN to a + nonzero value before calling this function with P == NULL. For + example: + + int *p = NULL; + size_t used = 0; + size_t allocated = 0; + size_t allocated1 = 1000; + + void + append_int (int value) + { + if (used == allocated) + { + p = x2nrealloc (p, &allocated1, sizeof *p); + allocated = allocated1; + } + p[used++] = value; + } + + */ + +static inline void * +x2nrealloc_inline (void *p, size_t *pn, size_t s) +{ + size_t n = *pn; + + if (! p) + { + if (! n) + { + /* The approximate size to use for initial small allocation + requests, when the invoking code specifies an old size of + zero. 64 bytes is the largest "small" request for the + GNU C library malloc. */ + enum { DEFAULT_MXFAST = 64 }; + + n = DEFAULT_MXFAST / s; + n += !n; + } + } + else + { + if (SIZE_MAX / 2 / s < n) + xalloc_die (); + n *= 2; + } + + *pn = n; + return xrealloc (p, n * s); +} + +void * +x2nrealloc (void *p, size_t *pn, size_t s) +{ + return x2nrealloc_inline (p, pn, s); +} + +/* If P is null, allocate a block of at least *PN bytes; otherwise, + reallocate P so that it contains more than *PN bytes. *PN must be + nonzero unless P is null. Set *PN to the new block's size, and + return the pointer to the new block. *PN is never set to zero, and + the returned pointer is never null. */ + +void * +x2realloc (void *p, size_t *pn) +{ + return x2nrealloc_inline (p, pn, 1); +} + +/* Allocate S bytes of zeroed memory dynamically, with error checking. + There's no need for xnzalloc (N, S), since it would be equivalent + to xcalloc (N, S). */ + +void * +xzalloc (size_t s) +{ + return memset (xmalloc (s), 0, s); +} + +/* Allocate zeroed memory for N elements of S bytes, with error + checking. S must be nonzero. */ + +void * +xcalloc (size_t n, size_t s) +{ + void *p; + /* Test for overflow, since some calloc implementations don't have + proper overflow checks. */ + if (xalloc_oversized (n, s) || ! (p = calloc (n, s))) + xalloc_die (); + return p; +} + +/* Clone an object P of size S, with error checking. There's no need + for xnclone (P, N, S), since xclone (P, N * S) works without any + need for an arithmetic overflow check. */ + +void * +xclone (void const *p, size_t s) +{ + return memcpy (xmalloc (s), p, s); +} diff --git a/contrib/cvs-1.12.9/lib/xselect.h b/contrib/cvs-1.12.9/lib/xselect.h new file mode 100644 index 0000000000..5605cbdb2a --- /dev/null +++ b/contrib/cvs-1.12.9/lib/xselect.h @@ -0,0 +1,21 @@ +/* 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. */ + +/* This file simply performs the include magic necessary for using select */ + +/* select also requires , "xtime.h", and */ + +#ifdef HAVE_SYS_BSDTYPES_H +# include +#endif + +#if HAVE_SYS_SELECT_H +# include +#endif diff --git a/contrib/cvs-1.12.9/lib/xsize.h b/contrib/cvs-1.12.9/lib/xsize.h new file mode 100644 index 0000000000..7634c6d4d5 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/xsize.h @@ -0,0 +1,108 @@ +/* xsize.h -- Checked size_t computations. + + Copyright (C) 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _XSIZE_H +#define _XSIZE_H + +/* Get size_t. */ +#include + +/* Get SIZE_MAX. */ +#include +#if HAVE_STDINT_H +# include +#endif + +/* The size of memory objects is often computed through expressions of + type size_t. Example: + void* p = malloc (header_size + n * element_size). + These computations can lead to overflow. When this happens, malloc() + returns a piece of memory that is way too small, and the program then + crashes while attempting to fill the memory. + To avoid this, the functions and macros in this file check for overflow. + The convention is that SIZE_MAX represents overflow. + malloc (SIZE_MAX) is not guaranteed to fail -- think of a malloc + implementation that uses mmap --, it's recommended to use size_overflow_p() + or size_in_bounds_p() before invoking malloc(). + The example thus becomes: + size_t size = xsum (header_size, xtimes (n, element_size)); + void *p = (size_in_bounds_p (size) ? malloc (size) : NULL); +*/ + +/* Convert an arbitrary value >= 0 to type size_t. */ +#define xcast_size_t(N) \ + ((N) <= SIZE_MAX ? (size_t) (N) : SIZE_MAX) + +/* Sum of two sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum (size_t size1, size_t size2) +{ + size_t sum = size1 + size2; + return (sum >= size1 ? sum : SIZE_MAX); +} + +/* Sum of three sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum3 (size_t size1, size_t size2, size_t size3) +{ + return xsum (xsum (size1, size2), size3); +} + +/* Sum of four sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum4 (size_t size1, size_t size2, size_t size3, size_t size4) +{ + return xsum (xsum (xsum (size1, size2), size3), size4); +} + +/* Maximum of two sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xmax (size_t size1, size_t size2) +{ + /* No explicit check is needed here, because for any n: + max (SIZE_MAX, n) == SIZE_MAX and max (n, SIZE_MAX) == SIZE_MAX. */ + return (size1 >= size2 ? size1 : size2); +} + +/* Multiplication of a count with an element size, with overflow check. + The count must be >= 0 and the element size must be > 0. + This is a macro, not an inline function, so that it works correctly even + when N is of a wider tupe and N > SIZE_MAX. */ +#define xtimes(N, ELSIZE) \ + ((N) <= SIZE_MAX / (ELSIZE) ? (size_t) (N) * (ELSIZE) : SIZE_MAX) + +/* Check for overflow. */ +#define size_overflow_p(SIZE) \ + ((SIZE) == SIZE_MAX) +/* Check against overflow. */ +#define size_in_bounds_p(SIZE) \ + ((SIZE) != SIZE_MAX) + +#endif /* _XSIZE_H */ diff --git a/contrib/cvs-1.12.9/lib/xstrdup.c b/contrib/cvs-1.12.9/lib/xstrdup.c new file mode 100644 index 0000000000..f04d32c9ef --- /dev/null +++ b/contrib/cvs-1.12.9/lib/xstrdup.c @@ -0,0 +1,34 @@ +/* xstrdup.c -- copy a string with out of memory checking + Copyright (C) 1990, 1996, 1998, 2001, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "xalloc.h" + +#include + +/* Return a newly allocated copy of STRING. */ + +char * +xstrdup (const char *string) +{ + if (string == NULL) return NULL; + return xclone (string, strlen (string) + 1); +} diff --git a/contrib/cvs-1.12.9/lib/xtime.h b/contrib/cvs-1.12.9/lib/xtime.h new file mode 100644 index 0000000000..00d9fae874 --- /dev/null +++ b/contrib/cvs-1.12.9/lib/xtime.h @@ -0,0 +1,57 @@ +/* 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. */ + +/* This file simply performs the include magic necessary for using time + * functions + */ + +#ifdef vms +# include +#else /* vms */ + +# if TIME_WITH_SYS_TIME +# include +# include +# else /* TIME_WITH_SYS_TIME */ +# if HAVE_SYS_TIME_H +# include +# else /* HAVE_SYS_TIME_H */ +# include +# endif /* !HAVE_SYS_TIME_H */ +# endif /* !TIME_WITH_SYS_TIME */ + +# ifdef HAVE_SYS_TIMEB_H +# include +# else /* HAVE_SYS_TIMEB_H */ +/* + * We use the obsolete `struct timeb' as part of our interface! + * Since the system doesn't have it, we define it here; + * our callers must do likewise. + * + * At the least we were using this in lib/getdate.y, but lib/system.h used to + * define it too, so maybe CVS is using it elsewhere. + */ +struct timeb { + time_t time; /* Seconds since the epoch */ + unsigned short millitm; /* Field not used */ + short timezone; /* Minutes west of GMT */ + short dstflag; /* Field not used */ +}; +# endif /* !HAVE_SYS_TIMEB_H */ + +# ifdef timezone +# undef timezone /* needed for sgi */ +# endif /* timezone */ + +# if !defined(HAVE_FTIME) && !defined(HAVE_TIMEZONE) +extern long timezone; +# endif /* !defined(HAVE_FTIME) && !defined(HAVE_TIMEZONE) */ + +#endif /* !vms */ diff --git a/contrib/cvs-1.12.9/lib/yesno.c b/contrib/cvs-1.12.9/lib/yesno.c new file mode 100644 index 0000000000..1e8793882d --- /dev/null +++ b/contrib/cvs-1.12.9/lib/yesno.c @@ -0,0 +1,38 @@ +/* yesno.c -- read a yes/no response from stdin + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +/* Read one line from standard input + and return nonzero if that line begins with y or Y, + otherwise return 0. */ + +int +yesno () +{ + int c; + int rv; + + fflush (stderr); + fflush (stdout); + c = getchar (); + rv = (c == 'y') || (c == 'Y'); + while (c != EOF && c != '\n') + c = getchar (); + + return rv; +} diff --git a/contrib/cvs-1.12.9/man/cvs.5 b/contrib/cvs-1.12.9/man/cvs.5 new file mode 100644 index 0000000000..589f0678f8 --- /dev/null +++ b/contrib/cvs-1.12.9/man/cvs.5 @@ -0,0 +1,330 @@ +.TH cvs 5 "12 February 1992" +.\" Full space in nroff; half space in troff +.de SP +.if n .sp +.if t .sp .5 +.. +.SH NAME +cvs \- Concurrent Versions System support files +.SH NOTE +This documentation may no longer be up to date. Please consult the Cederqvist +(CVS Manual) as specified in +.BR cvs ( 1 ). + +.SH SYNOPSIS +.hy 0 +.na +.TP +.B $CVSROOT/CVSROOT/commitinfo,v +.TP +.B $CVSROOT/CVSROOT/cvsignore,v +.TP +.B $CVSROOT/CVSROOT/cvswrappers,v +.TP +.B $CVSROOT/CVSROOT/editinfo,v +.TP +.B $CVSROOT/CVSROOT/history +.TP +.B $CVSROOT/CVSROOT/loginfo,v +.TP +.B $CVSROOT/CVSROOT/modules,v +.TP +.B $CVSROOT/CVSROOT/rcsinfo,v +.TP +.B $CVSROOT/CVSROOT/taginfo,v +.ad b +.hy 1 +.SH DESCRIPTION +.B cvs +is a system for providing source control to hierarchical collections +of source directories. Commands and procedures for using \fBcvs\fP +are described in +.BR cvs ( 1 ). +.SP +.B cvs +manages \fIsource repositories\fP, the directories containing master +copies of the revision-controlled files, by copying particular +revisions of the files to (and modifications back from) developers' +private \fIworking directories\fP. In terms of file structure, each +individual source repository is an immediate subdirectory of +\fB$CVSROOT\fP. +.SP +The files described here are supporting files; they do not have to +exist for \fBcvs\fP to operate, but they allow you to make \fBcvs\fP +operation more flexible. +.SP +You can use the `\|modules\|' file to define symbolic names for +collections of source maintained with \fBcvs\fP. If there is no +`\|modules\|' file, developers must specify complete path names +(absolute, or relative to \fB$CVSROOT\fP) for the files they wish to +manage with \fBcvs\fP commands. +.SP +You can use the `\|commitinfo\|' file to define programs to execute +whenever `\|\fBcvs commit\fP\|' is about to execute. +These programs are used for ``pre-commit'' checking to verify that the +modified, added, and removed files are really ready to be committed. +Some uses for this check might be to turn off a portion (or all) of the +source repository from a particular person or group. +Or, perhaps, to verify that the changed files conform to the site's +standards for coding practice. +.SP +You can use the `\|cvswrappers\|' file to record +.B cvs +wrapper commands to be used when checking files into and out of the +repository. Wrappers allow the file or directory to be processed +on the way in and out of CVS. The intended uses are many, one +possible use would be to reformat a C file before the file is checked +in, so all of the code in the repository looks the same. +.SP +You can use the `\|loginfo\|' file to define programs to execute after +any +.BR commit , +which writes a log entry for changes in the repository. +These logging programs might be used to append the log message to a file. +Or send the log message through electronic mail to a group of developers. +Or, perhaps, post the log message to a particular newsgroup. +.SP +You can use the `\|taginfo\|' file to define programs to execute after +any +.BR tag or rtag +operation. These programs might be used to append a message to a file +listing the new tag name and the programmer who created it, or send mail +to a group of developers, or, perhaps, post a message to a particular +newsgroup. +.SP +You can use the `\|rcsinfo\|' file to define forms for log messages. +.SP +You can use the `\|editinfo\|' file to define a program to execute for +editing/validating `\|\fBcvs commit\fP\|' log entries. +This is most useful when used with a `\|rcsinfo\|' forms specification, as +it can verify that the proper fields of the form have been filled in by the +user committing the change. +.SP +You can use the `\|cvsignore\|' file to specify the default list of +files to ignore during \fBupdate\fP. +.SP +You can use the `\|history\|' file to record the \fBcvs\fP commands +that affect the repository. +The creation of this file enables history logging. +.SH FILES +.TP +.B modules +The `\|modules\|' file records your definitions of names for +collections of source code. \fBcvs\fP will use these definitions if +you use \fBcvs\fP to check in a file with the right format to +`\|\fB$CVSROOT/CVSROOT/modules,v\fP\|'. +.SP +The `\|modules\|' file may contain blank lines and comments (lines +beginning with `\|\fB#\fP\|') as well as module definitions. +Long lines can be continued on the next line by specifying a backslash +(``\e'') as the last character on the line. +.SP +A \fImodule definition\fP is a single line of the `\|modules\|' file, +in either of two formats. In both cases, \fImname\fP represents the +symbolic module name, and the remainder of the line is its definition. +.SP +\fImname\fP \fB\-a\fP \fIaliases\fP\|.\|.\|. +.br +This represents the simplest way of defining a module \fImname\fP. +The `\|\fB\-a\fP\|' flags the definition as a simple alias: \fBcvs\fP +will treat any use of \fImname\fP (as a command argument) as if the list +of names \fIaliases\fP had been specified instead. \fIaliases\fP may +contain either other module names or paths. When you use paths in +\fIaliases\fP, `\|\fBcvs checkout\fP\|' creates all intermediate +directories in the working directory, just as if the path had been +specified explicitly in the \fBcvs\fP arguments. +.SP +.nf +\fImname\fP [ \fIoptions\fP ] \fIdir\fP [ \fIfiles\fP\|.\|.\|. ] [ \fB&\fP\fImodule\fP\|.\|.\|. ] +.fi +.SP +In the simplest case, this form of module definition reduces to +`\|\fImname dir\fP\|'. This defines all the files in directory +\fIdir\fP as module \fImname\fP. \fIdir\fP is a relative path (from +\fB$CVSROOT\fP) to a directory of source in one of the source +repositories. In this case, on \fBcheckout\fP, a single directory +called \fImname\fP is created as a working directory; no intermediate +directory levels are used by default, even if \fIdir\fP was a path +involving several directory levels. +.SP +By explicitly specifying \fIfiles\fP in the module definition after +\fIdir\fP, you can select particular files from directory +\fIdir\fP. The sample definition for \fBmodules\fP is an example of +a module defined with a single file from a particular directory. Here +is another example: +.SP +.nf +.ft B +m4test unsupported/gnu/m4 foreach.m4 forloop.m4 +.ft P +.fi +.SP +With this definition, executing `\|\fBcvs checkout m4test\fP\|' +will create a single working directory `\|m4test\|' containing the two +files listed, which both come from a common directory several levels +deep in the \fBcvs\fP source repository. +.SP +A module definition can refer to other modules by including +`\|\fB&\fP\fImodule\fP\|' in its definition. \fBcheckout\fP creates +a subdirectory for each such \fImodule\fP, in your working directory. +.br +.I +New in \fBcvs\fP 1.3; +avoid this feature if sharing module definitions with older versions +of \fBcvs\fP. +.SP +Finally, you can use one or more of the following \fIoptions\fP in +module definitions: +.SP +\&`\|\fB\-d\fP \fIname\fP\|', to name the working directory something +other than the module name. +.br +.I +New in \fBcvs\fP 1.3; +avoid this feature if sharing module definitions with older versions +of \fBcvs\fP. +.SP +\&`\|\fB\-i\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP +to run whenever files in a module are committed. \fIprog\fP runs with a +single argument, the full pathname of the affected directory in a +source repository. The `\|commitinfo\|', `\|loginfo\|', and +`\|editinfo\|' files provide other ways to call a program on \fBcommit\fP. +.SP +`\|\fB\-o\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP +to run whenever files in a module are checked out. \fIprog\fP runs +with a single argument, the module name. +.SP +`\|\fB\-e\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP +to run whenever files in a module are exported. \fIprog\fP runs +with a single argument, the module name. +.SP +`\|\fB\-t\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP +to run whenever files in a module are tagged. \fIprog\fP runs with two +arguments: the module name and the symbolic tag specified to \fBrtag\fP. +.SP +`\|\fB\-u\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP +to run whenever `\|\fBcvs update\fP\|' is executed from the top-level +directory of the checked-out module. \fIprog\fP runs with a +single argument, the full path to the source repository for this module. +.TP +\&\fBcommitinfo\fP, \fBloginfo\fP, \fBrcsinfo\fP, \fBeditinfo\fP +These files all specify programs to call at different points in the +`\|\fBcvs commit\fP\|' process. They have a common structure. +Each line is a pair of fields: a regular expression, separated by +whitespace from a filename or command-line template. +Whenever one of the regular expression matches a directory name in the +repository, the rest of the line is used. +If the line begins with a \fB#\fP character, the entire line is considered +a comment and is ignored. +Whitespace between the fields is also ignored. +.SP +For `\|loginfo\|', the rest of the +line is a command-line template to execute. +The templates can include not only +a program name, but whatever list of arguments you wish. If you write +`\|\fB%s\fP\|' somewhere on the argument list, \fBcvs\fP supplies, at +that point, the list of files affected by the \fBcommit\fP. +The first entry in the list is the relative path within the source +repository where the change is being made. +The remaining arguments list the files that are being modified, added, or +removed by this \fBcommit\fP invocation. +.SP +For `\|taginfo\|', the rest of the +line is a command-line template to execute. +The arguments passed to the command are, in order, the +.I tagname , +.I operation +(i.e. +.B add +for `tag', +.B mov +for `tag -F', and +.B del +for `tag -d`), +.I repository , +and any remaining are pairs of +.B "filename revision" . +A non-zero exit of the filter program will cause the tag to be aborted. +.SP +For `\|commitinfo\|', the rest of the line is a command-line template to +execute. +The template can include not only a program name, but whatever +list of arguments you wish. +The full path to the current source repository is appended to the template, +followed by the file names of any files involved in the commit (added, +removed, and modified files). +.SP +For `\|rcsinfo\|', the rest of the line is the full path to a file that +should be loaded into the log message template. +.SP +For `\|editinfo\|', the rest of the line is a command-line template to +execute. +The template can include not only a program name, but whatever +list of arguments you wish. +The full path to the current log message template file is appended to the +template. +.SP +You can use one of two special strings instead of a regular +expression: `\|\fBALL\fP\|' specifies a command line template that +must always be executed, and `\|\fBDEFAULT\fP\|' specifies a command +line template to use if no regular expression is a match. +.SP +The `\|commitinfo\|' file contains commands to execute \fIbefore\fP any +other \fBcommit\fP activity, to allow you to check any conditions that +must be satisfied before \fBcommit\fP can proceed. The rest of the +\fBcommit\fP will execute only if all selected commands from this file +exit with exit status \fB0\fP. +.SP +The `\|rcsinfo\|' file allows you to specify \fIlog templates\fP for +the \fBcommit\fP logging session; you can use this to provide a form +to edit when filling out the \fBcommit\fP log. The field after the +regular expression, in this file, contains filenames (of files +containing the logging forms) rather than command templates. +.SP +The `\|editinfo\|' file allows you to execute a script \fIbefore the +commit starts\fP, but after the log information is recorded. These +"edit" scripts can verify information recorded in the log file. If +the edit script exits with a non-zero exit status, the commit is aborted. +.SP +The `\|loginfo\|' file contains commands to execute \fIat the end\fP +of a commit. The text specified as a commit log message is piped +through the command; typical uses include sending mail, filing an +article in a newsgroup, or appending to a central file. +.TP +\&\fBcvsignore\fP, \fB.cvsignore\fP +The default list of files (or +.BR sh ( 1 ) +file name patterns) to ignore during `\|\fBcvs update\fP\|'. +At startup time, \fBcvs\fP loads the compiled in default list of file name +patterns (see +.BR cvs ( 1 )). +Then the per-repository list included in \fB$CVSROOT/CVSROOT/cvsignore\fP +is loaded, if it exists. +Then the per-user list is loaded from `\|$HOME/.cvsignore\|'. +Finally, as \fBcvs\fP traverses through your directories, it will load any +per-directory `\|.cvsignore\|' files whenever it finds one. +These per-directory files are only valid for exactly the directory that +contains them, not for any sub-directories. +.TP +.B history +Create this file in \fB$CVSROOT/CVSROOT\fP to enable history logging +(see the description of `\|\fBcvs history\fP\|'). +.SH "SEE ALSO" +.BR cvs ( 1 ), +.SH COPYING +Copyright \(co 1992 Cygnus Support, Brian Berliner, and Jeff Polk +.PP +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. +.PP +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. +.PP +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. diff --git a/contrib/cvs-1.12.9/man/cvsbug.8 b/contrib/cvs-1.12.9/man/cvsbug.8 new file mode 100644 index 0000000000..f5a96b3949 --- /dev/null +++ b/contrib/cvs-1.12.9/man/cvsbug.8 @@ -0,0 +1,243 @@ +.\" -*- nroff -*- +.\" --------------------------------------------------------------------------- +.\" man page for send-pr (by Heinz G. Seidl, hgs@cygnus.com) +.\" updated Feb 1993 for GNATS 3.00 by Jeffrey Osier, jeffrey@cygnus.com +.\" +.\" This file is part of the Problem Report Management System (GNATS) +.\" Copyright 1992 Cygnus Support +.\" +.\" 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. +.\" +.\" --------------------------------------------------------------------------- +.nh +.TH CVSBUG 8 xVERSIONx "February 1993" +.SH NAME +cvsbug \- send problem report (PR) about CVS to a central support site +.SH SYNOPSIS +.B cvsbug +[ +.I site +] +[ +.B \-f +.I problem-report +] +[ +.B \-t +.I mail-address +] +.br +.in +0.8i +[ +.B \-P +] +[ +.B \-L +] +[ +.B \-\-request-id +] +[ +.B \-v +] +.SH DESCRIPTION +.B cvsbug +is a tool used to submit +.I problem reports +.\" SITE ADMINISTRATORS - change this if you use a local default +(PRs) to a central support site. In most cases the correct +.I site +will be the default. This argument indicates the support site which +is responsible for the category of problem involved. Some sites may +use a local address as a default. +.I site +values are defined by using the +.BR aliases (5). +.LP +.B cvsbug +invokes an editor on a problem report template (after trying to fill +in some fields with reasonable default values). When you exit the +editor, +.B cvsbug +sends the completed form to the +.I Problem Report Management System +(\fBGNATS\fR) at a central support site. At the support site, the PR +is assigned a unique number and is stored in the \fBGNATS\fR database +according to its category and submitter-id. \fBGNATS\fR automatically +replies with an acknowledgement, citing the category and the PR +number. +.LP +To ensure that a PR is handled promptly, it should contain your (unique) +\fIsubmitter-id\fR and one of the available \fIcategories\fR to identify the +problem area. (Use +.B `cvsbug -L' +to see a list of categories.) +.LP +The +.B cvsbug +template at your site should already be customized with your +submitter-id (running `\|\fBinstall-sid\fP \fIsubmitter-id\fP\|' to +accomplish this is part of the installation procedures for +.BR cvsbug ). +If this hasn't been done, see your system administrator for your +submitter-id, or request one from your support site by invoking +.B `cvsbug \-\-request\-id'. +If your site does not distinguish between different user sites, or if +you are not affiliated with the support site, use +.B `net' +for this field. +.LP +The more precise your problem description and the more complete your +information, the faster your support team can solve your problems. +.SH OPTIONS +.TP +.BI \-f " problem-report" +specify a file (\fIproblem-report\fR) which already contains a +complete problem report. +.B cvsbug +sends the contents of the file without invoking the editor. If +the value for +.I problem-report +is +.BR `\|\-\|' , +then +.B cvsbug +reads from standard input. +.TP +.BI \-t " mail-address" +Change mail address at the support site for problem reports. The +default +.I mail-address +is the address used for the default +.IR site . +Use the +.I site +argument rather than this option in nearly all cases. +.TP +.B \-P +print the form specified by the environment variable +.B PR_FORM +on standard output. If +.B PR_FORM +is not set, print the standard blank PR template. No mail is sent. +.TP +.B -L +print the list of available categories. No mail is sent. +.TP +.B \-\-request\-id +sends mail to the default support site, or +.I site +if specified, with a request for your +.IR submitter-id . +If you are +not affiliated with +.IR site , +use a +.I submitter-id +of +.BR net \|'. +.TP +.B \-v +Display the +.B cvsbug +version number. +.LP +Note: use +.B cvsbug +to submit problem reports rather than mailing them directly. Using +both the template and +.B cvsbug +itself will help ensure all necessary information will reach the +support site. +.SH ENVIRONMENT +The environment variable +.B EDITOR +specifies the editor to invoke on the template. +.br +default: +.B vi +.sp +If the environment variable +.B PR_FORM +is set, then its value is used as the file name of the template for +your problem-report editing session. You can use this to start with a +partially completed form (for example, a form with the identification +fields already completed). +.SH "HOW TO FILL OUT A PROBLEM REPORT" +Problem reports have to be in a particular form so that a program can +easily manage them. Please remember the following guidelines: +.IP \(bu 3m +describe only +.B one problem +with each problem report. +.IP \(bu 3m +For follow-up mail, use the same subject line as the one in the automatic +acknowledgement. It consists of category, PR number and the original synopsis +line. This allows the support site to relate several mail messages to a +particular PR and to record them automatically. +.IP \(bu 3m +Please try to be as accurate as possible in the subject and/or synopsis line. +.IP \(bu 3m +The subject and the synopsis line are not confidential. This is +because open-bugs lists are compiled from them. Avoid confidential +information there. +.LP +See the GNU +.B Info +file +.B cvsbug.info +or the document \fIReporting Problems With cvsbug\fR\ for detailed +information on reporting problems +.SH "HOW TO SUBMIT TEST CASES, CODE, ETC." +Submit small code samples with the PR. Contact the support site for +instructions on submitting larger test cases and problematic source +code. +.SH FILES +.ta \w'/tmp/pbad$$ 'u +/tmp/p$$ copy of PR used in editing session +.br +/tmp/pf$$ copy of empty PR form, for testing purposes +.br +/tmp/pbad$$ file for rejected PRs +.SH INSTALLATION AND CONFIGURATION +See +.B INSTALL +for installation instructions. +.SH SEE ALSO +.BR gnats (l), +.BR query-pr (1), +.BR edit-pr (1), +.BR gnats (8), +.BR queue-pr (8), +.BR at-pr (8), +.BR mkcat (8), +.BR mkdist (8). +.SH AUTHORS +Jeffrey Osier, Brendan Kehoe, Jason Merrill, Heinz G. Seidl (Cygnus +Support) +.SH COPYING +Copyright (c) 1992, 1993 Free Software Foundation, Inc. +.PP +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. +.PP +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. +.PP +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. + diff --git a/contrib/cvs-1.12.9/src/add.c b/contrib/cvs-1.12.9/src/add.c new file mode 100644 index 0000000000..85b629a22f --- /dev/null +++ b/contrib/cvs-1.12.9/src/add.c @@ -0,0 +1,919 @@ +/* + * 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 source distribution. + * + * Add + * + * Adds a file or directory to the RCS source repository. For a file, + * the entry is marked as "needing to be added" in the user's own CVS + * directory, and really added to the repository when it is committed. + * For a directory, it is added at the appropriate place in the source + * repository and a CVS directory is generated within the directory. + * + * The -m option is currently the only supported option. Some may wish to + * supply standard "rcs" options here, but I've found that this causes more + * trouble than anything else. + * + * The user files or directories must already exist. For a directory, it must + * not already have a CVS file in it. + * + * An "add" on a file that has been "remove"d but not committed will cause the + * file to be resurrected. + */ + +#include +#include "cvs.h" +#include "savecwd.h" +#include "fileattr.h" + +static int add_directory (struct file_info *finfo); +static int build_entry (const char *repository, const char *user, + const char *options, const char *message, + List * entries, const char *tag); + +static const char *const add_usage[] = +{ + "Usage: %s %s [-k rcs-kflag] [-m message] files...\n", + "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n", + "\t-m\tUse \"message\" for the creation log.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +add (int argc, char **argv) +{ + char *message = NULL; + int i; + char *repository; + int c; + int err = 0; + int added_files = 0; + char *options = NULL; + List *entries; + Vers_TS *vers; + struct saved_cwd cwd; + /* Nonzero if we found a slash, and are thus adding files in a + subdirectory. */ + int found_slash = 0; + size_t cvsroot_len; + + if (argc == 1 || argc == -1) + usage (add_usage); + + wrap_setup (); + + /* parse args */ + optind = 0; + while ((c = getopt (argc, argv, "+k:m:")) != -1) + { + switch (c) + { + case 'k': + if (options) + free (options); + options = RCS_check_kflag (optarg); + break; + + case 'm': + message = xstrdup (optarg); + break; + case '?': + default: + usage (add_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (argc <= 0) + usage (add_usage); + + cvsroot_len = strlen (current_parsed_root->directory); + + /* First some sanity checks. I know that the CVS case is (sort of) + also handled by add_directory, but we need to check here so the + client won't get all confused in send_file_names. */ + for (i = 0; i < argc; i++) + { + int skip_file = 0; + + /* If it were up to me I'd probably make this a fatal error. + But some people are really fond of their "cvs add *", and + don't seem to object to the warnings. + Whatever. */ + strip_trailing_slashes (argv[i]); + if (strcmp (argv[i], ".") == 0 + || strcmp (argv[i], "..") == 0 + || fncmp (argv[i], CVSADM) == 0) + { + if (!quiet) + error (0, 0, "cannot add special file `%s'; skipping", argv[i]); + skip_file = 1; + } + else + { + char *p; + p = argv[i]; + while (*p != '\0') + { + if (ISSLASH (*p)) + { + found_slash = 1; + break; + } + ++p; + } + } + + if (skip_file) + { + int j; + + /* FIXME: We don't do anything about free'ing argv[i]. But + the problem is that it is only sometimes allocated (see + cvsrc.c). */ + + for (j = i; j < argc - 1; ++j) + argv[j] = argv[j + 1]; + --argc; + /* Check the new argv[i] again. */ + --i; + ++err; + } + } + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + int j; + + if (argc == 0) + /* We snipped out all the arguments in the above sanity + check. We can just forget the whole thing (and we + better, because if we fired up the server and passed it + nothing, it would spit back a usage message). */ + return err; + + start_server (); + ign_setup (); + if (options) + { + send_arg (options); + free (options); + } + option_with_arg ("-m", message); + send_arg ("--"); + + /* If !found_slash, refrain from sending "Directory", for + CVS 1.9 compatibility. If we only tried to deal with servers + which are at least CVS 1.9.26 or so, we wouldn't have to + special-case this. */ + if (found_slash) + { + repository = Name_Repository (NULL, NULL); + send_a_repository ("", repository, ""); + free (repository); + } + + for (j = 0; j < argc; ++j) + { + /* 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[j])) + { + char *tag; + char *date; + int nonbranch; + char *rcsdir; + char *p; + char *update_dir; + /* This is some mungeable storage into which we can point + with p and/or update_dir. */ + char *filedir; + + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + + filedir = xstrdup (argv[j]); + /* Deliberately discard the const below since we know we just + * allocated filedir and can do what we like with it. + */ + p = (char *)last_component (filedir); + if (p == filedir) + { + update_dir = ""; + } + else + { + p[-1] = '\0'; + update_dir = filedir; + if (CVS_CHDIR (update_dir) < 0) + error (1, errno, + "could not chdir to `%s'", update_dir); + } + + /* find the repository associated with our current dir */ + repository = Name_Repository (NULL, update_dir); + + /* don't add stuff to Emptydir */ + if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0 + && ISSLASH (repository[cvsroot_len]) + && strncmp (repository + cvsroot_len + 1, + CVSROOTADM, + sizeof CVSROOTADM - 1) == 0 + && ISSLASH (repository[cvsroot_len + sizeof CVSROOTADM]) + && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1, + CVSNULLREPOS) == 0) + error (1, 0, "cannot add to `%s'", repository); + + /* before we do anything else, see if we have any + per-directory tags */ + ParseTag (&tag, &date, &nonbranch); + + rcsdir = xmalloc (strlen (repository) + strlen (p) + 5); + sprintf (rcsdir, "%s/%s", repository, p); + + Create_Admin (p, argv[j], rcsdir, tag, date, + nonbranch, 0, 1); + + if (found_slash) + send_a_repository ("", repository, update_dir); + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + + if (tag) + free (tag); + if (date) + free (date); + free (rcsdir); + + if (p == filedir) + Subdir_Register ((List *) NULL, (char *) NULL, argv[j]); + else + { + Subdir_Register ((List *) NULL, update_dir, p); + } + free (repository); + free (filedir); + } + } + send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server ("add\012", 0); + if (message) + free (message); + return err + get_responses_and_close (); + } +#endif + + /* walk the arg list adding files/dirs */ + for (i = 0; i < argc; i++) + { + int begin_err = err; +#ifdef SERVER_SUPPORT + int begin_added_files = added_files; +#endif + struct file_info finfo; + char *filename, *p; + + memset (&finfo, 0, sizeof finfo); + + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + + finfo.fullname = xstrdup (argv[i]); + filename = xstrdup (argv[i]); + /* We know we can discard the const below since we just allocated + * filename and can do as we like with it. + */ + p = (char *)last_component (filename); + if (p == filename) + { + finfo.update_dir = ""; + finfo.file = p; + } + else + { + p[-1] = '\0'; + finfo.update_dir = filename; + finfo.file = p; + if (CVS_CHDIR (finfo.update_dir) < 0) + error (1, errno, "could not chdir to `%s'", finfo.update_dir); + } + + /* Add wrappers for this directory. They exist only until + the next call to wrap_add_file. */ + wrap_add_file (CVSDOTWRAPPER, 1); + + finfo.rcs = NULL; + + /* Find the repository associated with our current dir. */ + repository = Name_Repository (NULL, finfo.update_dir); + + /* don't add stuff to Emptydir */ + if (strncmp (repository, current_parsed_root->directory, + cvsroot_len) == 0 + && ISSLASH (repository[cvsroot_len]) + && strncmp (repository + cvsroot_len + 1, + CVSROOTADM, + sizeof CVSROOTADM - 1) == 0 + && ISSLASH (repository[cvsroot_len + sizeof CVSROOTADM]) + && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1, + CVSNULLREPOS) == 0) + error (1, 0, "cannot add to `%s'", repository); + + entries = Entries_Open (0, NULL); + + finfo.repository = repository; + finfo.entries = entries; + + /* 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 */ + if (vers->vn_rcs == NULL) + { + /* There is no RCS file either */ + if (vers->ts_user == NULL) + { + /* There is no user file either */ + error (0, 0, "nothing known about `%s'", finfo.fullname); + err++; + } + else if (!isdir (finfo.file) + || wrap_name_has (finfo.file, WRAP_TOCVS)) + { + /* + * See if a directory exists in the repository with + * the same name. If so, blow this request off. + */ + char *dname = xmalloc (strlen (repository) + + strlen (finfo.file) + + 10); + (void) sprintf (dname, "%s/%s", repository, finfo.file); + if (isdir (dname)) + { + error (0, 0, + "cannot add file `%s' since the directory", + finfo.fullname); + error (0, 0, "`%s' already exists in the repository", + dname); + error (1, 0, "invalid filename overlap"); + } + free (dname); + + if (vers->options == NULL || *vers->options == '\0') + { + /* 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 (finfo.file, WRAP_RCSOPTION)) + { + if (vers->options) + free (vers->options); + vers->options = wrap_rcsoption (finfo.file, 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, finfo.file, 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 (finfo.file, + WRAP_TOCVS) + ? "wrapper" + : "file"), + finfo.fullname, vers->tag); + else + error (0, 0, + "scheduling %s `%s' for addition", + (wrap_name_has (finfo.file, + WRAP_TOCVS) + ? "wrapper" + : "file"), + finfo.fullname); + } + } + } + } + } + else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) + { + if (isdir (finfo.file) + && !wrap_name_has (finfo.file, WRAP_TOCVS)) + { + error (0, 0, + "the directory `%s' cannot be added because a file" + " of the", finfo.fullname); + error (1, 0, "same name already exists in the repository."); + } + else + { + if (vers->nonbranch) + { + error (0, 0, + "cannot add file on non-branch tag `%s'", + vers->tag); + ++err; + } + else + { + char *timestamp = NULL; + if (vers->ts_user == NULL) + { + /* If this file does not exist locally, assume that + * the last version on the branch is being + * resurrected. + * + * Compute previous revision. We assume that it + * exists and that it is not a revision on the + * trunk of the form X.1 (1.1, 2.1, 3.1, ...). We + * also assume that it is not dead, which seems + * fair since we know vers->vn_rcs is dead + * and we shouldn't see two dead revisions in a + * row. + */ + char *prev = previous_rev (vers->srcfile, + vers->vn_rcs); + int status; + assert (prev != NULL); + if (!quiet) + error (0, 0, +"Resurrecting file `%s' from revision %s.", + finfo.fullname, prev); + status = RCS_checkout (vers->srcfile, finfo.file, + prev, vers->tag, + vers->options, RUN_TTY, + NULL, NULL); + xchmod (finfo.file, 1); + if (status != 0) + { + error (0, 0, "Failed to resurrect revision %s", + prev); + err++; + } + else + { + /* I don't actually set vers->ts_user here + * because it would confuse server_update(). + */ + timestamp = time_stamp (finfo.file); + if (!really_quiet) + write_letter (&finfo, 'U'); + } + free (prev); + } + if (!quiet) + { + char *bbuf; + if (vers->tag) + { + bbuf = xmalloc (strlen (vers->tag) + 14); + sprintf (bbuf, " on branch `%s'", vers->tag); + } + else + bbuf = ""; + error (0, 0, +"Re-adding file `%s'%s after dead revision %s.", + finfo.fullname, bbuf, vers->vn_rcs); + if (vers->tag) + free (bbuf); + } + Register (entries, finfo.file, "0", + timestamp ? timestamp : vers->ts_user, + vers->options, vers->tag, vers->date, NULL); + if (timestamp) free (timestamp); +#ifdef SERVER_SUPPORT + if (server_active && vers->ts_user == NULL) + { + /* If we resurrected the file from the archive, we + * need to tell the client about it. + */ + server_updated (&finfo, vers, + SERVER_UPDATED, + (mode_t) -1, NULL, NULL); + /* This is kinda hacky or, at least, it renders the + * name "begin_added_files" obsolete, but we want + * the added_files to be counted without triggering + * the check that causes server_checked_in() to be + * called below since we have already called + * server_updated() to complete the resurrection. + */ + ++begin_added_files; + } +#endif + ++added_files; + } + } + } + else + { + /* + * There is an RCS file already, so somebody else must've + * added it + */ + error (0, 0, "`%s' added independently by second party", + finfo.fullname); + err++; + } + } + else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') + { + + /* + * An entry for a new-born file, ts_rcs is dummy, but that is + * inappropriate here + */ + if (!quiet) + error (0, 0, "`%s' has already been entered", finfo.fullname); + err++; + } + else if (vers->vn_user[0] == '-') + { + /* An entry for a removed file, ts_rcs is invalid */ + if (vers->ts_user == NULL) + { + /* There is no user file (as it should be) */ + if (vers->vn_rcs == NULL) + { + + /* + * There is no RCS file, so somebody else must've removed + * it from under us + */ + error (0, 0, + "cannot resurrect `%s'; RCS file removed by" + " second party", finfo.fullname); + err++; + } + else + { + int status; + /* + * There is an RCS file, so remove the "-" from the + * version number and restore the file + */ + char *tmp = xmalloc (strlen (vers->vn_user)); + (void) strcpy (tmp, vers->vn_user + 1); + (void) strcpy (vers->vn_user, tmp); + free( tmp ); + status = RCS_checkout (vers->srcfile, finfo.file, + vers->vn_user, vers->tag, + vers->options, RUN_TTY, + NULL, NULL); + xchmod (finfo.file, 1); + if (status != 0) + { + error (0, 0, "Failed to resurrect revision %s.", + vers->vn_user); + err++; + tmp = NULL; + } + else + { + /* I don't actually set vers->ts_user here because it + * would confuse server_update(). + */ + tmp = time_stamp (finfo.file); + write_letter (&finfo, 'U'); + if (!quiet) + error (0, 0, "`%s', version %s, resurrected", + finfo.fullname, vers->vn_user); + } + Register (entries, finfo.file, vers->vn_user, + tmp, vers->options, + vers->tag, vers->date, NULL); + if (tmp) free (tmp); +#ifdef SERVER_SUPPORT + if (server_active) + { + /* If we resurrected the file from the archive, we + * need to tell the client about it. + */ + server_updated (&finfo, vers, + SERVER_UPDATED, + (mode_t) -1, NULL, NULL); + } + /* We don't increment added_files here because this isn't + * a change that needs to be committed. + */ +#endif + } + } + else + { + /* The user file shouldn't be there */ + error (0, 0, "\ +`%s' should be removed and is still there (or is back again)", finfo.fullname); + err++; + } + } + else + { + /* A normal entry, ts_rcs is valid, so it must already be there */ + if (!quiet) + error (0, 0, "`%s' already exists, with version number %s", + finfo.fullname, + vers->vn_user); + err++; + } + freevers_ts (&vers); + + /* passed all the checks. Go ahead and add it if its a directory */ + if (begin_err == err + && isdir (finfo.file) + && !wrap_name_has (finfo.file, WRAP_TOCVS)) + { + err += add_directory (&finfo); + } + else + { +#ifdef SERVER_SUPPORT + if (server_active && begin_added_files != added_files) + server_checked_in (finfo.file, finfo.update_dir, repository); +#endif + } + free (repository); + Entries_Close (entries); + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + + /* It's okay to discard the const to free this - we allocated this + * above. The const is for everybody else. + */ + free ((char *) finfo.fullname); + free ((char *) filename); + } + if (added_files && !really_quiet) + error (0, 0, "use `%s commit' to add %s permanently", + program_name, + (added_files == 1) ? "this file" : "these files"); + + if (message) + free (message); + if (options) + free (options); + + return err; +} + + + +/* + * The specified user file is really a directory. So, let's make sure that + * it is created in the RCS source repository, and that the user's directory + * is updated to include a CVS directory. + * + * Returns 1 on failure, 0 on success. + */ +static int +add_directory (struct file_info *finfo) +{ + const char *repository = finfo->repository; + List *entries = finfo->entries; + const char *dir = finfo->file; + + char *rcsdir = NULL; + struct saved_cwd cwd; + char *message = NULL; + char *tag, *date; + int nonbranch; + char *attrs; + + if (strchr (dir, '/') != NULL) + { + /* "Can't happen". */ + error (0, 0, + "directory %s not added; must be a direct sub-directory", dir); + return 1; + } + if (fncmp (dir, CVSADM) == 0) + { + error (0, 0, "cannot add a `%s' directory", CVSADM); + return 1; + } + + /* before we do anything else, see if we have any per-directory tags */ + ParseTag (&tag, &date, &nonbranch); + + /* Remember the default attributes from this directory, so we can apply + them to the new directory. */ + fileattr_startdir (repository); + attrs = fileattr_getall (NULL); + fileattr_free (); + + /* now, remember where we were, so we can get back */ + if (save_cwd (&cwd)) + return 1; + if (CVS_CHDIR (dir) < 0) + { + error (0, errno, "cannot chdir to %s", finfo->fullname); + return 1; + } +#ifdef SERVER_SUPPORT + if (!server_active && isfile (CVSADM)) +#else + if (isfile (CVSADM)) +#endif + { + error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM); + goto out; + } + + rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5); + sprintf (rcsdir, "%s/%s", repository, dir); + if (isfile (rcsdir) && !isdir (rcsdir)) + { + error (0, 0, "%s is not a directory; %s not added", rcsdir, + finfo->fullname); + goto out; + } + + /* setup the log message */ + message = xmalloc (strlen (rcsdir) + + 36 + + (tag == NULL ? 0 : strlen (tag) + 38) + + (date == NULL ? 0 : strlen (date) + 39)); + (void) sprintf (message, "Directory %s added to the repository\n", + rcsdir); + if (tag) + { + (void) strcat (message, "--> Using per-directory sticky tag `"); + (void) strcat (message, tag); + (void) strcat (message, "'\n"); + } + if (date) + { + (void) strcat (message, "--> Using per-directory sticky date `"); + (void) strcat (message, date); + (void) strcat (message, "'\n"); + } + + if (!isdir (rcsdir)) + { + mode_t omask; + Node *p; + List *ulist; + struct logfile_info *li; + + /* 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. */ + + if (!noexec) + { + omask = umask (cvsumask); + if (CVS_MKDIR (rcsdir, 0777) < 0) + { + error (0, errno, "cannot mkdir %s", rcsdir); + (void) umask (omask); + goto out; + } + (void) umask (omask); + } + + /* Now set the default file attributes to the ones we inherited + from the parent directory. */ + fileattr_startdir (rcsdir); + fileattr_setall (NULL, attrs); + fileattr_write (); + fileattr_free (); + if (attrs != NULL) + free (attrs); + + /* + * Set up an update list with a single title node for Update_Logfile + */ + ulist = getlist (); + p = getnode (); + p->type = UPDATE; + p->delproc = update_delproc; + p->key = xstrdup ("- New directory"); + 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 = li; + (void) addnode (ulist, p); + Update_Logfile (rcsdir, message, (FILE *) NULL, ulist); + dellist (&ulist); + } + +#ifdef SERVER_SUPPORT + if (server_active) + WriteTemplate (finfo->fullname, 1, rcsdir); + else +#endif + Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1); + + if (tag) + free (tag); + if (date) + free (date); + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + + Subdir_Register (entries, (char *) NULL, dir); + + if (!really_quiet) + cvs_output (message, 0); + + free (rcsdir); + free (message); + + return 0; + +out: + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + if (rcsdir != NULL) + free (rcsdir); + return 0; +} + + + +/* + * Builds an entry for a new file and sets up "CVS/file",[pt] by + * interrogating the user. Returns non-zero on error. + */ +static int +build_entry (const char *repository, const char *user, const char *options, + const char *message, List *entries, const char *tag) +{ + char *fname; + char *line; + FILE *fp; + + if (noexec) + return 0; + + /* + * The requested log is read directly from the user and stored in the + * file user,t. If the "message" argument is set, use it as the + * initial creation log (which typically describes the file). + */ + 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; +} + + + +/* vim:tabstop=8:shiftwidth=4 + */ diff --git a/contrib/cvs-1.12.9/src/admin.c b/contrib/cvs-1.12.9/src/admin.c new file mode 100644 index 0000000000..39b690705e --- /dev/null +++ b/contrib/cvs-1.12.9/src/admin.c @@ -0,0 +1,935 @@ +/* + * 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 source distribution. + * + * Administration ("cvs admin") + * + */ + +#include "cvs.h" +#ifdef CVS_ADMIN_GROUP +#include +#endif + +static Dtype admin_dirproc (void *callerdat, const char *dir, + const char *repos, const char *update_dir, + List *entries); +static int admin_fileproc (void *callerdat, struct file_info *finfo); + +char *UserAdminOptions = "k"; + +static const char *const admin_usage[] = +{ + "Usage: %s %s [options] files...\n", + "\t-a users Append (comma-separated) user names to access list.\n", + "\t-A file Append another file's access list.\n", + "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n", + "\t-c string Set comment leader.\n", + "\t-e[users] Remove (comma-separated) user names from access list\n", + "\t (all names if omitted).\n", + "\t-I Run interactively.\n", + "\t-k subst Set keyword substitution mode:\n", + "\t kv (Default) Substitute keyword and value.\n", + "\t kvl Substitute keyword, value, and locker (if any).\n", + "\t k Substitute keyword only.\n", + "\t o Preserve original string.\n", + "\t b Like o, but mark file as binary.\n", + "\t v Substitute value only.\n", + "\t-l[rev] Lock revision (latest revision on branch,\n", + "\t latest revision on trunk if omitted).\n", + "\t-L Set strict locking.\n", + "\t-m rev:msg Replace revision's log message.\n", + "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n", + "\t delete the tag; if rev is omitted, tag the latest\n", + "\t revision on the default branch.\n", + "\t-N tag[:[rev]] Same as -n except override existing tag.\n", + "\t-o range Delete (outdate) specified range of revisions:\n", + "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n", + "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n", + "\t rev: rev and following revisions on the same branch.\n", + "\t rev:: After rev on the same branch.\n", + "\t :rev rev and previous revisions on the same branch.\n", + "\t ::rev Before rev on the same branch.\n", + "\t rev Just rev.\n", + "\t-q Run quietly.\n", + "\t-s state[:rev] Set revision state (latest revision on branch,\n", + "\t latest revision on trunk if omitted).\n", + "\t-t[file] Get descriptive text from file (stdin if omitted).\n", + "\t-t-string Set descriptive text.\n", + "\t-u[rev] Unlock the revision (latest revision on branch,\n", + "\t latest revision on trunk if omitted).\n", + "\t-U Unset strict locking.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +/* This structure is used to pass information through start_recursion. */ +struct admin_data +{ + /* Set default branch (-b). It is "-b" followed by the value + given, or NULL if not specified, or merely "-b" if -b is + specified without a value. */ + char *branch; + + /* Set comment leader (-c). It is "-c" followed by the value + given, or NULL if not specified. The comment leader is + relevant only for old versions of RCS, but we let people set it + anyway. */ + char *comment; + + /* Set strict locking (-L). */ + int set_strict; + + /* Set nonstrict locking (-U). */ + int set_nonstrict; + + /* Delete revisions (-o). It is "-o" followed by the value specified. */ + char *delete_revs; + + /* Keyword substitution mode (-k), e.g. "-kb". */ + char *kflag; + + /* Description (-t). */ + char *desc; + + /* Interactive (-I). Problematic with client/server. */ + int interactive; + + /* This is the cheesy part. It is a vector with the options which + we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future + this presumably will be replaced by other variables which break + out the data in a more convenient fashion. AV as well as each of + the strings it points to is malloc'd. */ + int ac; + char **av; + int av_alloc; +}; + +/* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the + argument to that option, or NULL if omitted (whether NULL can actually + happen depends on whether the option was specified as optional to + getopt). */ +static void +arg_add (struct admin_data *dat, int opt, char *arg) +{ + char *newelt = xmalloc ((arg == NULL ? 0 : strlen (arg)) + 3); + strcpy (newelt, "-"); + newelt[1] = opt; + if (arg == NULL) + newelt[2] = '\0'; + else + strcpy (newelt + 2, arg); + + if (dat->av_alloc == 0) + { + dat->av_alloc = 1; + dat->av = (char **) xmalloc (dat->av_alloc * sizeof (*dat->av)); + } + else if (dat->ac >= dat->av_alloc) + { + dat->av_alloc *= 2; + dat->av = (char **) xrealloc (dat->av, + dat->av_alloc * sizeof (*dat->av)); + } + dat->av[dat->ac++] = newelt; +} + +int +admin (int argc, char **argv) +{ + int err; +#ifdef CVS_ADMIN_GROUP + struct group *grp; + struct group *getgrnam(const char *); +#endif + struct admin_data admin_data; + int c; + int i; + int only_allowed_options; + + if (argc <= 1) + usage (admin_usage); + + wrap_setup (); + + memset (&admin_data, 0, sizeof admin_data); + + /* TODO: get rid of `-' switch notation in admin_data. For + example, admin_data->branch should be not `-bfoo' but simply `foo'. */ + + optind = 0; + only_allowed_options = 1; + while ((c = getopt (argc, argv, + "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1) + { + if (c != 'q' && !strchr(UserAdminOptions, c)) + only_allowed_options = 0; + + switch (c) + { + case 'i': + /* This has always been documented as useless in cvs.texinfo + and it really is--admin_fileproc silently does nothing + if vers->vn_user is NULL. */ + error (0, 0, "the -i option to admin is not supported"); + error (0, 0, "run add or import to create an RCS file"); + goto usage_error; + + case 'b': + if (admin_data.branch != NULL) + { + error (0, 0, "duplicate 'b' option"); + goto usage_error; + } + if (optarg == NULL) + admin_data.branch = xstrdup ("-b"); + else + { + admin_data.branch = xmalloc (strlen (optarg) + 5); + strcpy (admin_data.branch, "-b"); + strcat (admin_data.branch, optarg); + } + break; + + case 'c': + if (admin_data.comment != NULL) + { + error (0, 0, "duplicate 'c' option"); + goto usage_error; + } + admin_data.comment = xmalloc (strlen (optarg) + 5); + strcpy (admin_data.comment, "-c"); + strcat (admin_data.comment, optarg); + break; + + case 'a': + arg_add (&admin_data, 'a', optarg); + break; + + case 'A': + /* In the client/server case, this is cheesy because + we just pass along the name of the RCS file, which + then will want to exist on the server. This is + accidental; having the client specify a pathname on + the server is not a design feature of the protocol. */ + arg_add (&admin_data, 'A', optarg); + break; + + case 'e': + arg_add (&admin_data, 'e', optarg); + break; + + case 'l': + /* Note that multiple -l options are valid. */ + arg_add (&admin_data, 'l', optarg); + break; + + case 'u': + /* Note that multiple -u options are valid. */ + arg_add (&admin_data, 'u', optarg); + break; + + case 'L': + /* Probably could also complain if -L is specified multiple + times, although RCS doesn't and I suppose it is reasonable + just to have it mean the same as a single -L. */ + if (admin_data.set_nonstrict) + { + error (0, 0, "-U and -L are incompatible"); + goto usage_error; + } + admin_data.set_strict = 1; + break; + + case 'U': + /* Probably could also complain if -U is specified multiple + times, although RCS doesn't and I suppose it is reasonable + just to have it mean the same as a single -U. */ + if (admin_data.set_strict) + { + error (0, 0, "-U and -L are incompatible"); + goto usage_error; + } + admin_data.set_nonstrict = 1; + break; + + case 'n': + /* Mostly similar to cvs tag. Could also be parsing + the syntax of optarg, although for now we just pass + it to rcs as-is. Note that multiple -n options are + valid. */ + arg_add (&admin_data, 'n', optarg); + break; + + case 'N': + /* Mostly similar to cvs tag. Could also be parsing + the syntax of optarg, although for now we just pass + it to rcs as-is. Note that multiple -N options are + valid. */ + arg_add (&admin_data, 'N', optarg); + break; + + case 'm': + /* Change log message. Could also be parsing the syntax + of optarg, although for now we just pass it to rcs + as-is. Note that multiple -m options are valid. */ + arg_add (&admin_data, 'm', optarg); + break; + + case 'o': + /* Delete revisions. Probably should also be parsing the + syntax of optarg, so that the client can give errors + rather than making the server take care of that. + Other than that I'm not sure whether it matters much + whether we parse it here or in admin_fileproc. + + Note that multiple -o options are invalid, in RCS + as well as here. */ + + if (admin_data.delete_revs != NULL) + { + error (0, 0, "duplicate '-o' option"); + goto usage_error; + } + admin_data.delete_revs = xmalloc (strlen (optarg) + 5); + strcpy (admin_data.delete_revs, "-o"); + strcat (admin_data.delete_revs, optarg); + break; + + case 's': + /* Note that multiple -s options are valid. */ + arg_add (&admin_data, 's', optarg); + break; + + case 't': + if (admin_data.desc != NULL) + { + error (0, 0, "duplicate 't' option"); + goto usage_error; + } + if (optarg != NULL && optarg[0] == '-') + admin_data.desc = xstrdup (optarg + 1); + else + { + size_t bufsize = 0; + size_t len; + + get_file (optarg, optarg, "r", &admin_data.desc, + &bufsize, &len); + } + break; + + case 'I': + /* At least in RCS this can be specified several times, + with the same meaning as being specified once. */ + admin_data.interactive = 1; + break; + + case 'q': + /* Silently set the global really_quiet flag. This keeps admin in + * sync with the RCS man page and allows us to silently support + * older servers when necessary. + * + * Some logic says we might want to output a deprecation warning + * here, but I'm opting not to in order to stay quietly in sync + * with the RCS man page. + */ + really_quiet = 1; + break; + + case 'x': + error (0, 0, "the -x option has never done anything useful"); + error (0, 0, "RCS files in CVS always end in ,v"); + goto usage_error; + + case 'V': + /* No longer supported. */ + error (0, 0, "the `-V' option is obsolete"); + break; + + case 'k': + if (admin_data.kflag != NULL) + { + error (0, 0, "duplicate '-k' option"); + goto usage_error; + } + admin_data.kflag = RCS_check_kflag (optarg); + break; + default: + case '?': + /* getopt will have printed an error message. */ + + usage_error: + /* Don't use cvs_cmd_name; it might be "server". */ + error (1, 0, "specify %s -H admin for usage information", + program_name); + } + } + argc -= optind; + argv += optind; + +#ifdef CVS_ADMIN_GROUP + /* The use of `cvs admin -k' is unrestricted. However, any other + option is restricted if the group CVS_ADMIN_GROUP exists on the + server. */ + if ( +# ifdef CLIENT_SUPPORT + /* This is only "secure" on the server, since the user could edit the + * RCS file on a local host, but some people like this kind of + * check anyhow. The alternative would be to check only when + * (server_active) rather than when not on the client. + */ + !current_parsed_root->isremote && +# endif /* CLIENT_SUPPORT */ + !only_allowed_options && + (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL) + { +#ifdef HAVE_GETGROUPS + gid_t *grps; + int n; + + /* get number of auxiliary groups */ + n = getgroups (0, NULL); + if (n < 0) + error (1, errno, "unable to get number of auxiliary groups"); + grps = (gid_t *) xmalloc((n + 1) * sizeof *grps); + n = getgroups (n, grps); + if (n < 0) + error (1, errno, "unable to get list of auxiliary groups"); + grps[n] = getgid(); + for (i = 0; i <= n; i++) + if (grps[i] == grp->gr_gid) break; + free (grps); + if (i > n) + error (1, 0, "usage is restricted to members of the group %s", + CVS_ADMIN_GROUP); +#else + char *me = getcaller(); + char **grnam; + + for (grnam = grp->gr_mem; *grnam; grnam++) + if (strcmp (*grnam, me) == 0) break; + if (!*grnam && getgid() != grp->gr_gid) + error (1, 0, "usage is restricted to members of the group %s", + CVS_ADMIN_GROUP); +#endif + } +#endif /* defined CVS_ADMIN_GROUP */ + + for (i = 0; i < admin_data.ac; ++i) + { + assert (admin_data.av[i][0] == '-'); + switch (admin_data.av[i][1]) + { + case 'm': + case 'l': + case 'u': + check_numeric (&admin_data.av[i][2], argc, argv); + break; + default: + break; + } + } + if (admin_data.branch != NULL) + check_numeric (admin_data.branch + 2, argc, argv); + if (admin_data.delete_revs != NULL) + { + char *p; + + check_numeric (admin_data.delete_revs + 2, argc, argv); + p = strchr (admin_data.delete_revs + 2, ':'); + if (p != NULL && isdigit ((unsigned char) p[1])) + check_numeric (p + 1, argc, argv); + else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2])) + check_numeric (p + 2, argc, argv); + } + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + /* Note that option_with_arg does not work for us, because some + of the options must be sent without a space between the option + and its argument. */ + if (admin_data.interactive) + error (1, 0, "-I option not useful with client/server"); + if (admin_data.branch != NULL) + send_arg (admin_data.branch); + if (admin_data.comment != NULL) + send_arg (admin_data.comment); + if (admin_data.set_strict) + send_arg ("-L"); + if (admin_data.set_nonstrict) + send_arg ("-U"); + if (admin_data.delete_revs != NULL) + send_arg (admin_data.delete_revs); + if (admin_data.desc != NULL) + { + char *p = admin_data.desc; + send_to_server ("Argument -t-", 0); + while (*p) + { + if (*p == '\n') + { + send_to_server ("\012Argumentx ", 0); + ++p; + } + else + { + char *q = strchr (p, '\n'); + if (q == NULL) q = p + strlen (p); + send_to_server (p, q - p); + p = q; + } + } + send_to_server ("\012", 1); + } + /* Send this for all really_quiets since we know that it will be silently + * ignored when unneeded. This supports old servers. + */ + if (really_quiet) + send_arg ("-q"); + if (admin_data.kflag != NULL) + send_arg (admin_data.kflag); + + for (i = 0; i < admin_data.ac; ++i) + send_arg (admin_data.av[i]); + + send_arg ("--"); + send_files (argc, argv, 0, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server ("admin\012", 0); + err = get_responses_and_close (); + goto return_it; + } +#endif /* CLIENT_SUPPORT */ + + lock_tree_promotably (argc, argv, 0, W_LOCAL, 0); + + err = start_recursion + (admin_fileproc, NULL, admin_dirproc, + NULL, &admin_data, + argc, argv, 0, + W_LOCAL, 0, CVS_LOCK_WRITE, NULL, 1, NULL); + + Lock_Cleanup (); + + return_it: + if (admin_data.branch != NULL) + free (admin_data.branch); + if (admin_data.comment != NULL) + free (admin_data.comment); + if (admin_data.delete_revs != NULL) + free (admin_data.delete_revs); + if (admin_data.kflag != NULL) + free (admin_data.kflag); + if (admin_data.desc != NULL) + free (admin_data.desc); + for (i = 0; i < admin_data.ac; ++i) + free (admin_data.av[i]); + if (admin_data.av != NULL) + free (admin_data.av); + + return (err); +} + +/* + * Called to run "rcs" on a particular file. + */ +/* ARGSUSED */ +static int +admin_fileproc (void *callerdat, struct file_info *finfo) +{ + struct admin_data *admin_data = (struct admin_data *) callerdat; + Vers_TS *vers; + char *version; + int i; + int status = 0; + RCSNode *rcs, *rcs2; + + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); + + version = vers->vn_user; + if (version != NULL && strcmp (version, "0") == 0) + { + error (0, 0, "cannot admin newly added file `%s'", finfo->file); + status = 1; + goto exitfunc; + } + + rcs = vers->srcfile; + if (rcs == NULL) + { + if (!really_quiet) + error (0, 0, "nothing known about %s", finfo->file); + status = 1; + goto exitfunc; + } + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + if (!really_quiet) + { + cvs_output ("RCS file: ", 0); + cvs_output (rcs->path, 0); + cvs_output ("\n", 1); + } + + if (admin_data->branch != NULL) + { + char *branch = &admin_data->branch[2]; + if (*branch != '\0' && ! isdigit ((unsigned char) *branch)) + { + branch = RCS_whatbranch (rcs, admin_data->branch + 2); + if (branch == NULL) + { + error (0, 0, "%s: Symbolic name %s is undefined.", + rcs->path, admin_data->branch + 2); + status = 1; + } + } + if (status == 0) + RCS_setbranch (rcs, branch); + if (branch != NULL && branch != &admin_data->branch[2]) + free (branch); + } + if (admin_data->comment != NULL) + { + if (rcs->comment != NULL) + free (rcs->comment); + rcs->comment = xstrdup (admin_data->comment + 2); + } + if (admin_data->set_strict) + rcs->strict_locks = 1; + if (admin_data->set_nonstrict) + rcs->strict_locks = 0; + if (admin_data->delete_revs != NULL) + { + char *s, *t, *rev1, *rev2; + /* Set for :, clear for ::. */ + int inclusive; + char *t2; + + s = admin_data->delete_revs + 2; + inclusive = 1; + t = strchr (s, ':'); + if (t != NULL) + { + if (t[1] == ':') + { + inclusive = 0; + t2 = t + 2; + } + else + t2 = t + 1; + } + + /* Note that we don't support '-' for ranges. RCS considers it + obsolete and it is problematic with tags containing '-'. "cvs log" + has made the same decision. */ + + if (t == NULL) + { + /* -orev */ + rev1 = xstrdup (s); + rev2 = xstrdup (s); + } + else if (t == s) + { + /* -o:rev2 */ + rev1 = NULL; + rev2 = xstrdup (t2); + } + else + { + *t = '\0'; + rev1 = xstrdup (s); + *t = ':'; /* probably unnecessary */ + if (*t2 == '\0') + /* -orev1: */ + rev2 = NULL; + else + /* -orev1:rev2 */ + rev2 = xstrdup (t2); + } + + if (rev1 == NULL && rev2 == NULL) + { + /* RCS segfaults if `-o:' is given */ + error (0, 0, "no valid revisions specified in `%s' option", + admin_data->delete_revs); + status = 1; + } + else + { + status |= RCS_delete_revs (rcs, rev1, rev2, inclusive); + if (rev1) + free (rev1); + if (rev2) + free (rev2); + } + } + if (admin_data->desc != NULL) + { + free (rcs->desc); + rcs->desc = xstrdup (admin_data->desc); + } + if (admin_data->kflag != NULL) + { + char *kflag = admin_data->kflag + 2; + char *oldexpand = RCS_getexpand (rcs); + if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0) + RCS_setexpand (rcs, kflag); + } + + /* Handle miscellaneous options. TODO: decide whether any or all + of these should have their own fields in the admin_data + structure. */ + for (i = 0; i < admin_data->ac; ++i) + { + char *arg; + char *p, *rev, *revnum, *tag, *msg; + char **users; + int argc, u; + Node *n; + RCSVers *delta; + + arg = admin_data->av[i]; + switch (arg[1]) + { + case 'a': /* fall through */ + case 'e': + line2argv (&argc, &users, arg + 2, " ,\t\n"); + if (arg[1] == 'a') + for (u = 0; u < argc; ++u) + RCS_addaccess (rcs, users[u]); + else if (argc == 0) + RCS_delaccess (rcs, NULL); + else + for (u = 0; u < argc; ++u) + RCS_delaccess (rcs, users[u]); + free_names (&argc, users); + break; + case 'A': + + /* See admin-19a-admin and friends in sanity.sh for + relative pathnames. It makes sense to think in + terms of a syntax which give pathnames relative to + the repository or repository corresponding to the + current directory or some such (and perhaps don't + include ,v), but trying to worry about such things + is a little pointless unless you first worry about + whether "cvs admin -A" as a whole makes any sense + (currently probably not, as access lists don't + affect the behavior of CVS). */ + + rcs2 = RCS_parsercsfile (arg + 2); + if (rcs2 == NULL) + error (1, 0, "cannot continue"); + + p = xstrdup (RCS_getaccess (rcs2)); + line2argv (&argc, &users, p, " \t\n"); + free (p); + freercsnode (&rcs2); + + for (u = 0; u < argc; ++u) + RCS_addaccess (rcs, users[u]); + free_names (&argc, users); + break; + case 'n': /* fall through */ + case 'N': + if (arg[2] == '\0') + { + cvs_outerr ("missing symbolic name after ", 0); + cvs_outerr (arg, 0); + cvs_outerr ("\n", 1); + break; + } + p = strchr (arg, ':'); + if (p == NULL) + { + if (RCS_deltag (rcs, arg + 2) != 0) + { + error (0, 0, "%s: Symbolic name %s is undefined.", + rcs->path, + arg + 2); + status = 1; + continue; + } + break; + } + *p = '\0'; + tag = xstrdup (arg + 2); + *p++ = ':'; + + /* Option `n' signals an error if this tag is already bound. */ + if (arg[1] == 'n') + { + n = findnode (RCS_symbols (rcs), tag); + if (n != NULL) + { + error (0, 0, + "%s: symbolic name %s already bound to %s", + rcs->path, + tag, (char *)n->data); + status = 1; + free (tag); + continue; + } + } + + /* Attempt to perform the requested tagging. */ + + if ((*p == 0 && (rev = RCS_head (rcs))) + || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */ + { + RCS_check_tag (tag); /* exit if not a valid tag */ + RCS_settag (rcs, tag, rev); + free (rev); + } + else + { + if (!really_quiet) + error (0, 0, + "%s: Symbolic name or revision %s is undefined.", + rcs->path, p); + status = 1; + } + free (tag); + break; + case 's': + p = strchr (arg, ':'); + if (p == NULL) + { + tag = xstrdup (arg + 2); + rev = RCS_head (rcs); + } + else + { + *p = '\0'; + tag = xstrdup (arg + 2); + *p++ = ':'; + rev = xstrdup (p); + } + revnum = RCS_gettag (rcs, rev, 0, NULL); + if (revnum != NULL) + { + n = findnode (rcs->versions, revnum); + free (revnum); + } + else + n = NULL; + if (n == NULL) + { + error (0, 0, + "%s: can't set state of nonexisting revision %s", + rcs->path, + rev); + free (rev); + status = 1; + continue; + } + free (rev); + delta = n->data; + free (delta->state); + delta->state = tag; + break; + + case 'm': + p = strchr (arg, ':'); + if (p == NULL) + { + error (0, 0, "%s: -m option lacks revision number", + rcs->path); + status = 1; + continue; + } + *p = '\0'; /* temporarily make arg+2 its own string */ + rev = RCS_gettag (rcs, arg + 2, 1, NULL); /* Force tag match */ + if (rev == NULL) + { + error (0, 0, "%s: no such revision %s", rcs->path, arg+2); + status = 1; + *p = ':'; /* restore the full text of the -m argument */ + continue; + } + msg = p+1; + + n = findnode (rcs->versions, rev); + /* tags may exist against non-existing versions */ + if (n == NULL) + { + error (0, 0, "%s: no such revision %s: %s", + rcs->path, arg+2, rev); + status = 1; + *p = ':'; /* restore the full text of the -m argument */ + free (rev); + continue; + } + *p = ':'; /* restore the full text of the -m argument */ + free (rev); + + delta = n->data; + if (delta->text == NULL) + { + delta->text = (Deltatext *) xmalloc (sizeof (Deltatext)); + memset ((void *) delta->text, 0, sizeof (Deltatext)); + } + delta->text->version = xstrdup (delta->version); + delta->text->log = make_message_rcsvalid (msg); + break; + + case 'l': + status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0); + break; + case 'u': + status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0); + break; + default: assert(0); /* can't happen */ + } + } + + if (status == 0) + { + RCS_rewrite (rcs, NULL, NULL); + if (!really_quiet) + cvs_output ("done\n", 5); + } + else + { + /* Note that this message should only occur after another + message has given a more specific error. The point of this + additional message is to make it clear that the previous problems + caused CVS to forget about the idea of modifying the RCS file. */ + if (!really_quiet) + error (0, 0, "RCS file for `%s' not modified.", finfo->file); + RCS_abandon (rcs); + } + + exitfunc: + freevers_ts (&vers); + return status; +} + + + +/* + * Print a warm fuzzy message + */ +/* ARGSUSED */ +static Dtype +admin_dirproc (void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries) +{ + if (!quiet) + error (0, 0, "Administrating %s", update_dir); + return (R_PROCESS); +} diff --git a/contrib/cvs-1.12.9/src/annotate.c b/contrib/cvs-1.12.9/src/annotate.c new file mode 100644 index 0000000000..eebe7d8f82 --- /dev/null +++ b/contrib/cvs-1.12.9/src/annotate.c @@ -0,0 +1,283 @@ +/* + * 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 source distribution. + * + * Show last revision where each line modified + * + * Prints the specified files with each line annotated with the revision + * number where it was last modified. With no argument, annotates all + * all the files in the directory (recursive by default). + */ + +#include "cvs.h" + +/* Options from the command line. */ + +static int force_tag_match = 1; +static int force_binary = 0; +static char *tag = NULL; +static int tag_validated; +static char *date = NULL; + +static int is_rannotate; + +static int annotate_fileproc (void *callerdat, struct file_info *); +static int rannotate_proc (int argc, char **argv, char *xwhere, + char *mwhere, char *mfile, int shorten, + int local, char *mname, char *msg); + +static const char *const annotate_usage[] = +{ + "Usage: %s %s [-lRfF] [-r rev] [-D date] [files...]\n", + "\t-l\tLocal directory only, no recursion.\n", + "\t-R\tProcess directories recursively.\n", + "\t-f\tUse head revision if tag/date not found.\n", + "\t-F\tAnnotate binary files.\n", + "\t-r rev\tAnnotate file as of specified revision/tag.\n", + "\t-D date\tAnnotate file as of specified date.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +/* Command to show the revision, date, and author where each line of a + file was modified. */ + +int +annotate (int argc, char **argv) +{ + int local = 0; + int err = 0; + int c; + + is_rannotate = (strcmp(cvs_cmd_name, "rannotate") == 0); + + if (argc == -1) + usage (annotate_usage); + + optind = 0; + while ((c = getopt (argc, argv, "+lr:D:fFR")) != -1) + { + switch (c) + { + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case 'r': + tag = optarg; + break; + case 'D': + date = Make_Date (optarg); + break; + case 'f': + force_tag_match = 0; + break; + case 'F': + force_binary = 1; + break; + case '?': + default: + usage (annotate_usage); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + start_server (); + + if (is_rannotate && !supported_request ("rannotate")) + error (1, 0, "server does not support rannotate"); + + ign_setup (); + + if (local) + send_arg ("-l"); + if (!force_tag_match) + send_arg ("-f"); + if (force_binary) + send_arg ("-F"); + option_with_arg ("-r", tag); + if (date) + client_senddate (date); + send_arg ("--"); + if (is_rannotate) + { + int i; + for (i = 0; i < argc; i++) + send_arg (argv[i]); + send_to_server ("rannotate\012", 0); + } + else + { + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server ("annotate\012", 0); + } + return get_responses_and_close (); + } +#endif /* CLIENT_SUPPORT */ + + if (is_rannotate) + { + DBM *db; + int i; + db = open_module (); + for (i = 0; i < argc; i++) + { + err += do_module (db, argv[i], MISC, "Annotating", rannotate_proc, + (char *) NULL, 0, local, 0, 0, (char *) NULL); + } + close_module (db); + } + else + { + err = rannotate_proc (argc + 1, argv - 1, (char *) NULL, + (char *) NULL, (char *) NULL, 0, local, (char *) NULL, + (char *) NULL); + } + + return err; +} + + +static int +rannotate_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile, int shorten, int local, char *mname, char *msg) +{ + /* Begin section which is identical to patch_proc--should this + be abstracted out somehow? */ + char *myargv[2]; + int err = 0; + int which; + char *repository; + char *where; + + if (is_rannotate) + { + repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); + (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); + where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1) + + 1); + (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; + + /* if the portion of the module is a path, put the dir part on repos */ + if ((cp = strrchr (mfile, '/')) != NULL) + { + *cp = '\0'; + (void) strcat (repository, "/"); + (void) strcat (repository, mfile); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + mfile = cp + 1; + } + + /* take care of the rest */ + path = xmalloc (strlen (repository) + strlen (mfile) + 5); + (void) sprintf (path, "%s/%s", repository, mfile); + if (isdir (path)) + { + /* directory means repository gets the dir tacked on */ + (void) strcpy (repository, path); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + } + else + { + myargv[0] = argv[0]; + myargv[1] = mfile; + argc = 2; + argv = myargv; + } + free (path); + } + + /* cd to the starting repository */ + if ( CVS_CHDIR (repository) < 0) + { + error (0, errno, "cannot chdir to %s", repository); + free (repository); + return (1); + } + /* End section which is identical to patch_proc. */ + + if (force_tag_match && tag != NULL) + which = W_REPOS | W_ATTIC; + else + which = W_REPOS; + } + else + { + where = NULL; + which = W_LOCAL; + repository = ""; + } + + if (tag != NULL && !tag_validated) + { + tag_check_valid (tag, argc - 1, argv + 1, local, 0, repository); + tag_validated = 1; + } + + err = start_recursion ( annotate_fileproc, (FILESDONEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, + argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ, + where, 1, repository ); + if ( which & W_REPOS ) + free ( repository ); + if ( where != NULL ) + free (where); + return err; +} + + +static int +annotate_fileproc (void *callerdat, struct file_info *finfo) +{ + char *expand, *version; + + if (finfo->rcs == NULL) + return (1); + + if (finfo->rcs->flags & PARTIAL) + RCS_reparsercsfile (finfo->rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + expand = RCS_getexpand (finfo->rcs); + version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, + (int *) NULL); + + if (version == NULL) + return 0; + + /* Distinguish output for various files if we are processing + several files. */ + cvs_outerr ("\nAnnotations for ", 0); + cvs_outerr (finfo->fullname, 0); + cvs_outerr ("\n***************\n", 0); + + if (!force_binary && expand && expand[0] == 'b') + { + cvs_outerr ("Skipping binary file -- -F not specified.\n", 0); + } + else + { + RCS_deltas (finfo->rcs, (FILE *) NULL, (struct rcsbuffer *) NULL, + version, RCS_ANNOTATE, NULL, NULL, NULL, NULL); + } + free (version); + return 0; +} diff --git a/contrib/cvs-1.12.9/src/buffer.c b/contrib/cvs-1.12.9/src/buffer.c new file mode 100644 index 0000000000..20938ef26d --- /dev/null +++ b/contrib/cvs-1.12.9/src/buffer.c @@ -0,0 +1,1880 @@ +/* Code for the buffer data structure. */ + +#include "cvs.h" +#include "buffer.h" + +#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) + +# ifdef HAVE_WINSOCK_H +# include +# else +# include +# endif + +/* 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 buf_default_memory_error (struct buffer *); +static void allocate_buffer_datas (void); +static struct buffer_data *get_buffer_data (void); + +/* Initialize a buffer structure. */ + +struct buffer * +buf_initialize (int (*input) (void *, char *, int, int, int *), + int (*output) (void *, const char *, int, int *), + int (*flush) (void *), + int (*block) (void *, int), + int (*shutdown) (struct buffer *), + void (*memory) (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 ? memory : buf_default_memory_error; + buf->closure = closure; + return buf; +} + +/* Free a buffer structure. */ + +void +buf_free (struct buffer *buf) +{ + if (buf->closure != NULL) + { + free (buf->closure); + buf->closure = NULL; + } + if (buf->data != NULL) + { + buf->last->next = free_buffer_data; + free_buffer_data = buf->data; + } + free (buf); +} + +/* Initialize a buffer structure which is not to be used for I/O. */ + +struct buffer * +buf_nonio_initialize( void (*memory) (struct buffer *) ) +{ + return (buf_initialize + ((int (*) (void *, char *, int, int, int *)) NULL, + (int (*) (void *, const char *, int, int *)) NULL, + (int (*) (void *)) NULL, + (int (*) (void *, int)) NULL, + (int (*) (struct buffer *)) NULL, + memory, + (void *) NULL)); +} + +/* Default memory error handler. */ + +static void +buf_default_memory_error (struct buffer *buf) +{ + error (1, 0, "out of memory"); +} + +/* Allocate more buffer_data structures. */ + +static void +allocate_buffer_datas (void) +{ + struct buffer_data *alc; + char *space; + int i; + + /* Allocate buffer_data structures in blocks of 16. */ +# define ALLOC_COUNT (16) + + alc = ((struct buffer_data *) + xmalloc (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 (void) +{ + 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 and its file descriptor is empty. */ +int +buf_empty (buf) + struct buffer *buf; +{ + /* Try and read any data on the file descriptor first. + * We already know the descriptor is non-blocking. + */ + buf_input_data (buf, NULL); + return buf_empty_p (buf); +} + + + +/* See whether a buffer is empty. */ +int +buf_empty_p (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 xmalloc'ed chunk BUFFER_DATA_SIZE. + */ + +int +buf_count_mem (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 (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 (struct buffer *buf, const char *string) +{ + buf_output (buf, string, strlen (string)); +} + +/* Add a single character to BUF. */ + +void +buf_append_char (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 (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 (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 (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 (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 (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 (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 (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; + } +} + +/* Append the data on one buffer to another. This removes the data + from the source buffer. */ + +void +buf_append_buffer (struct buffer *to, struct buffer *from) +{ + buf_append_data (to, from->data, from->last); + from->data = NULL; + from->last = NULL; +} + +/* + * 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 (FILE *f, long int 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 (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 (struct buffer_data *buf) +{ + int size = 0; + while (buf) + { + size += buf->size; + buf = buf->next; + } + return size; +} + +/* Return the number of bytes in a buffer. */ + +int +buf_length (struct buffer *buf) +{ + return buf_chain_length (buf->data); +} + +/* + * 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 (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 (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 = xmalloc (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 (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 (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 (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 (struct buffer *buf) +{ + if (buf->shutdown) + return (*buf->shutdown) (buf); + 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 (void *, char *, int, int, int *); +static int stdio_buffer_output (void *, const char *, int, int *); +static int stdio_buffer_flush (void *); +static int stdio_buffer_shutdown (struct buffer *buf); + + + +/* Initialize a buffer built on a stdio FILE. */ +struct stdio_buffer_closure +{ + FILE *fp; + int child_pid; +}; + + + +struct buffer * +stdio_buffer_initialize (FILE *fp, int child_pid, int input, + void (*memory) (struct buffer *)) +{ + struct stdio_buffer_closure *bc = xmalloc (sizeof (*bc)); + + bc->fp = fp; + bc->child_pid = child_pid; + + return buf_initialize (input ? stdio_buffer_input : NULL, + input ? NULL : stdio_buffer_output, + input ? NULL : stdio_buffer_flush, + NULL, stdio_buffer_shutdown, memory, + bc); +} + + + +/* Return the file associated with a stdio buffer. */ +FILE * +stdio_buffer_get_file (struct buffer *buf) +{ + struct stdio_buffer_closure *bc; + + assert (buf->shutdown == stdio_buffer_shutdown); + + bc = buf->closure; + + return bc->fp; +} + + + +/* The buffer input function for a buffer built on a stdio FILE. */ +static int +stdio_buffer_input (void *closure, char *data, int need, int size, int *got) +{ + struct stdio_buffer_closure *bc = 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 (bc->fp); + + if (ch == EOF) + { + if (feof (bc->fp)) + return -1; + else if (errno == 0) + return EIO; + else + return errno; + } + + *data = ch; + *got = 1; + return 0; + } + + nbytes = fread (data, 1, need, bc->fp); + + if (nbytes == 0) + { + *got = 0; + if (feof (bc->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 (void *closure, const char *data, int have, int *wrote) +{ + struct stdio_buffer_closure *bc = closure; + + *wrote = 0; + + while (have > 0) + { + int nbytes; + + nbytes = fwrite (data, 1, have, bc->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 (void *closure) +{ + struct stdio_buffer_closure *bc = (struct stdio_buffer_closure *) closure; + + if (fflush (bc->fp) != 0) + { + if (errno == 0) + return EIO; + else + return errno; + } + + return 0; +} + + + +static int +stdio_buffer_shutdown (struct buffer *buf) +{ + struct stdio_buffer_closure *bc = buf->closure; + struct stat s; + int closefp = 1; + + /* Must be a pipe or a socket. What could go wrong? */ + assert (fstat (fileno (bc->fp), &s) != -1); + + /* Flush the buffer if we can */ + if (buf->flush) + { + buf_flush (buf, 1); + buf->flush = NULL; + } + + if (buf->input) + { + /* There used to be a check here for unread data in the buffer of on + * the pipe, but it was deemed unnecessary and possibly dangerous. In + * some sense it could be second-guessing the caller who requested it + * closed, as well. + */ + +# ifdef SHUTDOWN_SERVER + if (current_parsed_root->method != server_method) +# endif +# ifndef NO_SOCKET_TO_FD + { + /* shutdown() sockets */ + if (S_ISSOCK (s.st_mode)) + shutdown (fileno (bc->fp), 0); + } +# endif /* NO_SOCKET_TO_FD */ +# ifdef START_RSH_WITH_POPEN_RW + /* Can't be set with SHUTDOWN_SERVER defined */ + else if (pclose (bc->fp) == EOF) + { + error (1, errno, "closing connection to %s", + current_parsed_root->hostname); + closefp = 0; + } +# endif /* START_RSH_WITH_POPEN_RW */ + + buf->input = NULL; + } + else if (buf->output) + { +# ifdef SHUTDOWN_SERVER + /* FIXME: Should have a SHUTDOWN_SERVER_INPUT & + * SHUTDOWN_SERVER_OUTPUT + */ + if (current_parsed_root->method == server_method) + SHUTDOWN_SERVER (fileno (bc->fp)); + else +# endif +# ifndef NO_SOCKET_TO_FD + /* shutdown() sockets */ + if (S_ISSOCK (s.st_mode)) + shutdown (fileno (bc->fp), 1); +# else + { + /* I'm not sure I like this empty block, but the alternative + * is a another nested NO_SOCKET_TO_FD switch above. + */ + } +# endif /* NO_SOCKET_TO_FD */ + + buf->output = NULL; + } + + if (closefp && fclose (bc->fp) == EOF) + { + if (0 +# ifdef SERVER_SUPPORT + || server_active +# endif /* SERVER_SUPPORT */ + ) + { + /* Syslog this? */ + } +# ifdef CLIENT_SUPPORT + else + error (1, errno, + "closing down connection to %s", + current_parsed_root->hostname); +# endif /* CLIENT_SUPPORT */ + } + + /* If we were talking to a process, make sure it exited */ + if (bc->child_pid) + { + int w; + + do + w = waitpid (bc->child_pid, (int *) 0, 0); + while (w == -1 && errno == EINTR); + if (w == -1) + error (1, errno, "waiting for process %d", bc->child_pid); + } + return 0; +} + + + +/* Certain types of communication input and output data in packets, + where each packet is translated in some fashion. The packetizing + buffer type supports that, given a buffer which handles lower level + I/O and a routine to translate the data in a packet. + + This code uses two bytes for the size of a packet, so packets are + restricted to 65536 bytes in total. + + The translation functions should just translate; they may not + significantly increase or decrease the amount of data. The actual + size of the initial data is part of the translated data. The + output translation routine may add up to PACKET_SLOP additional + bytes, and the input translation routine should shrink the data + correspondingly. */ + +# define PACKET_SLOP (100) + +/* This structure is the closure field of a packetizing buffer. */ + +struct packetizing_buffer +{ + /* The underlying buffer. */ + struct buffer *buf; + /* The input translation function. Exactly one of inpfn and outfn + will be NULL. The input translation function should + untranslate the data in INPUT, storing the result in OUTPUT. + SIZE is the amount of data in INPUT, and is also the size of + OUTPUT. This should return 0 on success, or an errno code. */ + int (*inpfn) (void *fnclosure, const char *input, char *output, + int size); + /* The output translation function. This should translate the + data in INPUT, storing the result in OUTPUT. The first two + bytes in INPUT will be the size of the data, and so will SIZE. + This should set *TRANSLATED to the amount of translated data in + OUTPUT. OUTPUT is large enough to hold SIZE + PACKET_SLOP + bytes. This should return 0 on success, or an errno code. */ + int (*outfn) (void *fnclosure, const char *input, char *output, + int size, int *translated); + /* A closure for the translation function. */ + void *fnclosure; + /* For an input buffer, we may have to buffer up data here. */ + /* This is non-zero if the buffered data has been translated. + Otherwise, the buffered data has not been translated, and starts + with the two byte packet size. */ + int translated; + /* The amount of buffered data. */ + int holdsize; + /* The buffer allocated to hold the data. */ + char *holdbuf; + /* The size of holdbuf. */ + int holdbufsize; + /* If translated is set, we need another data pointer to track + where we are in holdbuf. If translated is clear, then this + pointer is not used. */ + char *holddata; +}; + + + +static int packetizing_buffer_input (void *, char *, int, int, int *); +static int packetizing_buffer_output (void *, const char *, int, int *); +static int packetizing_buffer_flush (void *); +static int packetizing_buffer_block (void *, int); +static int packetizing_buffer_shutdown (struct buffer *); + + + +/* Create a packetizing buffer. */ +struct buffer * +packetizing_buffer_initialize (struct buffer *buf, + int (*inpfn) (void *, const char *, char *, + int), + int (*outfn) (void *, const char *, char *, + int, int *), + void *fnclosure, + void (*memory) (struct buffer *)) +{ + struct packetizing_buffer *pb; + + pb = (struct packetizing_buffer *) xmalloc (sizeof *pb); + memset (pb, 0, sizeof *pb); + + pb->buf = buf; + pb->inpfn = inpfn; + pb->outfn = outfn; + pb->fnclosure = fnclosure; + + if (inpfn != NULL) + { + /* Add PACKET_SLOP to handle larger translated packets, and + add 2 for the count. This buffer is increased if + necessary. */ + pb->holdbufsize = BUFFER_DATA_SIZE + PACKET_SLOP + 2; + pb->holdbuf = xmalloc (pb->holdbufsize); + } + + return buf_initialize (inpfn != NULL ? packetizing_buffer_input : NULL, + inpfn != NULL ? NULL : packetizing_buffer_output, + inpfn != NULL ? NULL : packetizing_buffer_flush, + packetizing_buffer_block, + packetizing_buffer_shutdown, + memory, + pb); +} + + + +/* Input data from a packetizing buffer. */ +static int +packetizing_buffer_input (void *closure, char *data, int need, int size, + int *got) +{ + struct packetizing_buffer *pb = closure; + + *got = 0; + + if (pb->holdsize > 0 && pb->translated) + { + int copy; + + copy = pb->holdsize; + + if (copy > size) + { + memcpy (data, pb->holddata, size); + pb->holdsize -= size; + pb->holddata += size; + *got = size; + return 0; + } + + memcpy (data, pb->holddata, copy); + pb->holdsize = 0; + pb->translated = 0; + + data += copy; + need -= copy; + size -= copy; + *got = copy; + } + + while (need > 0 || *got == 0) + { + int get, status, nread, count, tcount; + char *bytes; + char stackoutbuf[BUFFER_DATA_SIZE + PACKET_SLOP]; + char *inbuf, *outbuf; + + /* If we don't already have the two byte count, get it. */ + if (pb->holdsize < 2) + { + get = 2 - pb->holdsize; + status = buf_read_data (pb->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) + pb->holdbuf[1] = bytes[0]; + else + { + pb->holdbuf[0] = bytes[0]; + if (nread < 2) + { + /* We only got one byte, but we needed two. Stash + the byte we got, and try again. */ + pb->holdsize = 1; + continue; + } + pb->holdbuf[1] = bytes[1]; + } + pb->holdsize = 2; + } + + /* Read the packet. */ + + count = (((pb->holdbuf[0] & 0xff) << 8) + + (pb->holdbuf[1] & 0xff)); + + if (count + 2 > pb->holdbufsize) + { + char *n; + + /* We didn't allocate enough space in the initialize + function. */ + + n = xrealloc (pb->holdbuf, count + 2); + if (n == NULL) + { + (*pb->buf->memory_error) (pb->buf); + return ENOMEM; + } + pb->holdbuf = n; + pb->holdbufsize = count + 2; + } + + get = count - (pb->holdsize - 2); + + status = buf_read_data (pb->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; + } + + if (nread < get) + { + /* We did not get all the data we need to fill the packet. + buf_read_data does not promise to return all the bytes + requested, so we must try again. */ + memcpy (pb->holdbuf + pb->holdsize, bytes, nread); + pb->holdsize += nread; + continue; + } + + /* We have a complete untranslated packet of COUNT bytes. */ + + if (pb->holdsize == 2) + { + /* We just read the entire packet (the 2 bytes in + PB->HOLDBUF are the size). Save a memcpy by + translating directly from BYTES. */ + inbuf = bytes; + } + else + { + /* We already had a partial packet in PB->HOLDBUF. We + need to copy the new data over to make the input + contiguous. */ + memcpy (pb->holdbuf + pb->holdsize, bytes, nread); + inbuf = pb->holdbuf + 2; + } + + if (count <= sizeof stackoutbuf) + outbuf = stackoutbuf; + else + { + outbuf = xmalloc (count); + if (outbuf == NULL) + { + (*pb->buf->memory_error) (pb->buf); + return ENOMEM; + } + } + + status = (*pb->inpfn) (pb->fnclosure, inbuf, outbuf, count); + if (status != 0) + return status; + + /* The first two bytes in the translated buffer are the real + length of the translated data. */ + tcount = ((outbuf[0] & 0xff) << 8) + (outbuf[1] & 0xff); + + if (tcount > count) + error (1, 0, "Input translation failure"); + + if (tcount > size) + { + /* We have more data than the caller has provided space + for. We need to save some of it for the next call. */ + + memcpy (data, outbuf + 2, size); + *got += size; + + pb->holdsize = tcount - size; + memcpy (pb->holdbuf, outbuf + 2 + size, tcount - size); + pb->holddata = pb->holdbuf; + pb->translated = 1; + + if (outbuf != stackoutbuf) + free (outbuf); + + return 0; + } + + memcpy (data, outbuf + 2, tcount); + + if (outbuf != stackoutbuf) + free (outbuf); + + pb->holdsize = 0; + + data += tcount; + need -= tcount; + size -= tcount; + *got += tcount; + } + + return 0; +} + + + +/* Output data to a packetizing buffer. */ +static int +packetizing_buffer_output (void *closure, const char *data, int have, + int *wrote) +{ + struct packetizing_buffer *pb = closure; + char inbuf[BUFFER_DATA_SIZE + 2]; + char stack_outbuf[BUFFER_DATA_SIZE + PACKET_SLOP + 4]; + struct buffer_data *outdata; + char *outbuf; + int size, status, translated; + + if (have > BUFFER_DATA_SIZE) + { + /* It would be easy to xmalloc 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); + + size = have + 2; + + /* The output function is permitted to add up to PACKET_SLOP + bytes, and we need 2 bytes for the size of the translated data. + If we can guarantee that the result will fit in a buffer_data, + we translate directly into one to avoid a memcpy in buf_output. */ + if (size + PACKET_SLOP + 2 > BUFFER_DATA_SIZE) + outbuf = stack_outbuf; + else + { + outdata = get_buffer_data (); + if (outdata == NULL) + { + (*pb->buf->memory_error) (pb->buf); + return ENOMEM; + } + + outdata->next = NULL; + outdata->bufp = outdata->text; + + outbuf = outdata->text; + } + + status = (*pb->outfn) (pb->fnclosure, inbuf, outbuf + 2, size, + &translated); + if (status != 0) + return status; + + /* The output function is permitted to add up to PACKET_SLOP + bytes. */ + if (translated > size + PACKET_SLOP) + abort (); + + outbuf[0] = (translated >> 8) & 0xff; + outbuf[1] = translated & 0xff; + + if (outbuf == stack_outbuf) + buf_output (pb->buf, outbuf, translated + 2); + else + { + outdata->size = translated + 2; + buf_append_data (pb->buf, outdata, outdata); + } + + *wrote = have; + + /* We will only be here because buf_send_output was called on the + packetizing buffer. That means that we should now call + buf_send_output on the underlying buffer. */ + return buf_send_output (pb->buf); +} + + + +/* Flush data to a packetizing buffer. */ +static int +packetizing_buffer_flush (void *closure) +{ + struct packetizing_buffer *pb = (struct packetizing_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 (pb->buf, 0); +} + + + +/* The block routine for a packetizing buffer. */ +static int +packetizing_buffer_block (void *closure, int block) +{ + struct packetizing_buffer *pb = (struct packetizing_buffer *) closure; + + if (block) + return set_block (pb->buf); + else + return set_nonblock (pb->buf); +} + + + +/* Shut down a packetizing buffer. */ +static int +packetizing_buffer_shutdown (struct buffer *buf) +{ + struct packetizing_buffer *pb = buf->closure; + + return buf_shutdown (pb->buf); +} + +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ diff --git a/contrib/cvs-1.12.9/src/buffer.h b/contrib/cvs-1.12.9/src/buffer.h new file mode 100644 index 0000000000..aec4e311f4 --- /dev/null +++ b/contrib/cvs-1.12.9/src/buffer.h @@ -0,0 +1,154 @@ +/* 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) (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) (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) (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) (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) (struct buffer *); + + /* 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) (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) + +/* The type of a function passed as a memory error handler. */ +typedef void (*BUFMEMERRPROC) (struct buffer *); + +extern struct buffer *buf_initialize (int (*) (void *, char *, int, + int, int *), + int (*) (void *, const char *, + int, int *), + int (*) (void *), + int (*) (void *, int), + int (*) (struct buffer *), + void (*) (struct buffer *), + void *); +extern void buf_free (struct buffer *); +extern struct buffer *buf_nonio_initialize (void (*) (struct buffer *)); +extern struct buffer *stdio_buffer_initialize + (FILE *, int, int, void (*) (struct buffer *)); +extern FILE *stdio_buffer_get_file (struct buffer *); +extern struct buffer *compress_buffer_initialize + (struct buffer *, int, int, void (*) (struct buffer *)); +extern struct buffer *packetizing_buffer_initialize + (struct buffer *, int (*) (void *, const char *, char *, int), + int (*) (void *, const char *, char *, int, int *), void *, + void (*) (struct buffer *)); +extern int buf_empty (struct buffer *); +extern int buf_empty_p (struct buffer *); +extern void buf_output (struct buffer *, const char *, int); +extern void buf_output0 (struct buffer *, const char *); +extern void buf_append_char (struct buffer *, int); +extern int buf_send_output (struct buffer *); +extern int buf_flush (struct buffer *, int); +extern int set_nonblock (struct buffer *); +extern int set_block (struct buffer *); +extern int buf_send_counted (struct buffer *); +extern int buf_send_special_count (struct buffer *, int); +extern void buf_append_data (struct buffer *, + struct buffer_data *, + struct buffer_data *); +extern void buf_append_buffer (struct buffer *, struct buffer *); +extern int buf_read_file (FILE *, long, struct buffer_data **, + struct buffer_data **); +extern int buf_read_file_to_eof (FILE *, struct buffer_data **, + struct buffer_data **); +extern int buf_input_data (struct buffer *, int *); +extern int buf_read_line (struct buffer *, char **, int *); +extern int buf_read_data (struct buffer *, int, char **, int *); +extern void buf_copy_lines (struct buffer *, struct buffer *, int); +extern int buf_copy_counted (struct buffer *, struct buffer *, int *); +extern int buf_chain_length (struct buffer_data *); +extern int buf_length (struct buffer *); +extern int buf_shutdown (struct buffer *); + +#ifdef SERVER_FLOWCONTROL +extern int buf_count_mem (struct buffer *); +#endif /* SERVER_FLOWCONTROL */ + +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ diff --git a/contrib/cvs-1.12.9/src/checkin.c b/contrib/cvs-1.12.9/src/checkin.c new file mode 100644 index 0000000000..3e524d8032 --- /dev/null +++ b/contrib/cvs-1.12.9/src/checkin.c @@ -0,0 +1,172 @@ +/* + * 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 source distribution. + * + * Check In + * + * Does a very careful checkin of the file "user", and tries not to spoil its + * modification time (to avoid needless recompilations). When RCS ID keywords + * get expanded on checkout, however, the modification time is updated and + * there is no good way to get around this. + * + * Returns non-zero on error. + */ + +#include "cvs.h" +#include "fileattr.h" +#include "edit.h" + +int +Checkin (int type, struct file_info *finfo, char *rev, char *tag, + char *options, char *message) +{ + Vers_TS *vers; + int set_time; + char *tocvsPath = NULL; + + tocvsPath = wrap_tocvs_process_file (finfo->file); + if (!noexec) + { + if (tocvsPath) + { + if (unlink_file_dir (finfo->file) < 0) + if (! existence_error (errno)) + error (1, errno, "cannot remove %s", finfo->fullname); + rename_file (tocvsPath, finfo->file); + } + } + + /* There use to be a check for finfo->rcs == NULL here and then a + * call to RCS_parse when necessary, but Checkin() isn't called + * if the RCS file hasn't already been parsed in one of the + * check functions. + */ + assert (finfo->rcs != NULL); + + switch (RCS_checkin (finfo->rcs, finfo->file, message, rev, + RCS_FLAGS_KEEPFILE)) + { + case 0: /* everything normal */ + + /* The checkin succeeded. If checking the file out again + would not cause any changes, we are done. Otherwise, + we need to check out the file, which will change the + modification time of the file. + + The only way checking out the file could cause any + changes is if the file contains RCS keywords. So we if + we are not expanding RCS keywords, we are done. */ + + if (strcmp (options, "-V4") == 0) /* upgrade to V5 now */ + options[0] = '\0'; + + /* FIXME: If PreservePermissions is on, RCS_cmp_file is + going to call RCS_checkout into a temporary file + anyhow. In that case, it would be more efficient to + call RCS_checkout here, compare the resulting files + using xcmp, and rename if necessary. I think this + should be fixed in RCS_cmp_file. */ + if( ( ! preserve_perms + && options != NULL + && ( strcmp( options, "-ko" ) == 0 + || strcmp( options, "-kb" ) == 0 ) ) + || RCS_cmp_file( finfo->rcs, rev, (char **)NULL, (char *)NULL, + options, finfo->file ) == 0 ) + { + /* The existing file is correct. We don't have to do + anything. */ + set_time = 0; + } + else + { + /* The existing file is incorrect. We need to check + out the correct file contents. */ + if (RCS_checkout (finfo->rcs, finfo->file, rev, (char *) NULL, + options, RUN_TTY, (RCSCHECKOUTPROC) NULL, + (void *) NULL) != 0) + error (1, 0, "failed when checking out new copy of %s", + finfo->fullname); + xchmod (finfo->file, 1); + set_time = 1; + } + + wrap_fromcvs_process_file (finfo->file); + + /* + * If we want read-only files, muck the permissions here, before + * getting the file time-stamp. + */ + if (!cvswrite || fileattr_get (finfo->file, "_watched")) + xchmod (finfo->file, 0); + + /* 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 (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_user, + vers->options, vers->tag, vers->date, (char *) 0); + history_write (type, NULL, vers->vn_rcs, + finfo->file, finfo->repository); + + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + + break; + + case -1: /* fork failed */ + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + + if (!noexec) + error (1, errno, "could not check in %s -- fork failed", + finfo->fullname); + return (1); + + default: /* ci failed */ + + /* The checkin failed, for some unknown reason, so we + print an error, and return an error. We assume that + the original file has not been touched. */ + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + + if (!noexec) + error (0, 0, "could not check in %s", finfo->fullname); + return (1); + } + + /* + * When checking in a specific revision, we may have locked the wrong + * branch, so to be sure, we do an extra unlock here before + * returning. + */ + if (rev) + { + (void) RCS_unlock (finfo->rcs, NULL, 1); + RCS_rewrite (finfo->rcs, NULL, NULL); + } + +#ifdef SERVER_SUPPORT + if (server_active) + { + if (set_time) + /* Need to update the checked out file on the client side. */ + server_updated (finfo, vers, SERVER_UPDATED, + (mode_t) -1, (unsigned char *) NULL, + (struct buffer *) NULL); + else + server_checked_in (finfo->file, finfo->update_dir, finfo->repository); + } + else +#endif + mark_up_to_date (finfo->file); + + freevers_ts (&vers); + return 0; +} diff --git a/contrib/cvs-1.12.9/src/checkout.c b/contrib/cvs-1.12.9/src/checkout.c new file mode 100644 index 0000000000..1206815ccb --- /dev/null +++ b/contrib/cvs-1.12.9/src/checkout.c @@ -0,0 +1,1239 @@ +/* + * 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 source distribution. + * + * Create Version + * + * "checkout" creates a "version" of an RCS repository. This version is owned + * totally by the user and is actually an independent copy, to be dealt with + * as seen fit. Once "checkout" has been called in a given directory, it + * never needs to be called again. The user can keep up-to-date by calling + * "update" when he feels like it; this will supply him with a merge of his + * own modifications and the changes made in the RCS original. See "update" + * for details. + * + * "checkout" can be given a list of directories or files to be updated and in + * the case of a directory, will recursivley create any sub-directories that + * exist in the repository. + * + * When the user is satisfied with his own modifications, the present version + * can be committed by "commit"; this keeps the present version in tact, + * usually. + * + * The call is cvs checkout [options] ... + * + * "checkout" creates a directory ./CVS, in which it keeps its administration, + * in two files, Repository and Entries. The first contains the name of the + * repository. The second contains one line for each registered file, + * consisting of the version number it derives from, its time stamp at + * derivation time and its name. Both files are normal files and can be + * edited by the user, if necessary (when the repository is moved, e.g.) + */ + +#include "cvs.h" + +static char *findslash (char *start, char *p); +static int checkout_proc (int argc, char **argv, char *where, + char *mwhere, char *mfile, int shorten, + int local_specified, char *omodule, + char *msg); + +static const char *const checkout_usage[] = +{ + "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 (avoids stickiness).\n", + "\t-s\tLike -c, but include module status.\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. (is sticky)\n", + "\t-j rev\tMerge in changes made between current revision and rev.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +static const char *const export_usage[] = +{ + "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\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", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +static int checkout_prune_dirs; +static int force_tag_match; +static int pipeout; +static int aflag; +static char *options; +static char *tag; +static int tag_validated; +static char *date; +static char *join_rev1; +static char *join_rev2; +static int join_tags_validated; +static char *preload_update_dir; +static char *history_name; +static enum mtype m_type; + +int +checkout (int argc, char **argv) +{ + int i; + int c; + DBM *db; + int cat = 0, err = 0, status = 0; + int run_module_prog = 1; + int local = 0; + int shorten = -1; + char *where = NULL; + char *valid_options; + const char *const *valid_usage; + + /* initialize static options */ + force_tag_match = 1; + if (options) + { + free (options); + options = NULL; + } + tag = date = join_rev1 = join_rev2 = preload_update_dir = NULL; + history_name = NULL; + tag_validated = join_tags_validated = 0; + + + /* + * A smaller subset of options are allowed for the export command, which + * is essentially like checkout, except that it hard-codes certain + * options to be default (like -kv) and takes care to remove the CVS + * directory when it has done its duty + */ + if (strcmp (cvs_cmd_name, "export") == 0) + { + m_type = EXPORT; + valid_options = "+Nnk:d:flRQqr:D:"; + valid_usage = export_usage; + } + else + { + m_type = CHECKOUT; + valid_options = "+ANnk:d:flRpQqcsr:D:j:P"; + valid_usage = checkout_usage; + } + + if (argc == -1) + usage (valid_usage); + + ign_setup (); + wrap_setup (); + + optind = 0; + while ((c = getopt (argc, argv, valid_options)) != -1) + { + switch (c) + { + case 'A': + aflag = 1; + break; + case 'N': + shorten = 0; + break; + case 'k': + if (options) + free (options); + options = RCS_check_kflag (optarg); + break; + case 'n': + run_module_prog = 0; + break; + case 'Q': + 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) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + cvs_cmd_name); + break; + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case 'P': + checkout_prune_dirs = 1; + break; + case 'p': + pipeout = 1; + run_module_prog = 0; /* don't run module prog when piping */ + noexec = 1; /* so no locks will be created */ + break; + case 'c': + cat = 1; + break; + case 'd': + where = optarg; + if (shorten == -1) + shorten = 1; + break; + case 's': + cat = status = 1; + break; + case 'f': + force_tag_match = 0; + break; + case 'r': + tag = optarg; + checkout_prune_dirs = 1; + break; + case 'D': + date = Make_Date (optarg); + checkout_prune_dirs = 1; + break; + case 'j': + if (join_rev2) + error (1, 0, "only two -j options can be specified"); + if (join_rev1) + join_rev2 = optarg; + else + join_rev1 = optarg; + break; + case '?': + default: + usage (valid_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (shorten == -1) + shorten = 0; + + if (cat && argc != 0) + error (1, 0, "-c and -s must not get any arguments"); + + if (!cat && argc == 0) + error (1, 0, "must specify at least one module or directory"); + + if (where && pipeout) + error (1, 0, "-d and -p are mutually exclusive"); + + if (m_type == EXPORT) + { + if (!tag && !date) + error (1, 0, "must specify a tag or date"); + + if (tag && isdigit ((unsigned char) tag[0])) + error (1, 0, "tag `%s' must be a symbolic tag", tag); + } + +#ifdef SERVER_SUPPORT + if (server_active && where != NULL) + { + server_pathname_check (where); + } +#endif + + if (!cat && !pipeout && !safe_location( where )) { + error(1, 0, "Cannot check out files into the repository itself"); + } + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + int expand_modules; + + start_server (); + + ign_setup (); + + expand_modules = (!cat && !pipeout + && 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"); + if (pipeout) + send_arg ("-p"); + if (!force_tag_match) + send_arg ("-f"); + if (aflag) + send_arg("-A"); + if (!shorten) + send_arg("-N"); + if (checkout_prune_dirs && m_type == CHECKOUT) + send_arg("-P"); + client_prune_dirs = checkout_prune_dirs; + if (cat && !status) + send_arg("-c"); + if (where != NULL) + option_with_arg ("-d", where); + if (status) + send_arg("-s"); + if (options != NULL && options[0] != '\0') + send_arg (options); + option_with_arg ("-r", tag); + if (date) + client_senddate (date); + if (join_rev1 != NULL) + option_with_arg ("-j", join_rev1); + if (join_rev2 != NULL) + option_with_arg ("-j", join_rev2); + send_arg ("--"); + + if (expand_modules) + { + client_send_expansions (local, where, 1); + } + else + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + client_nonexpanded_setup (); + } + + send_to_server (m_type == EXPORT ? "export\012" : "co\012", 0); + return get_responses_and_close (); + } +#endif /* CLIENT_SUPPORT */ + + if (cat) + { + cat_module (status); + if (options) + { + free (options); + options = NULL; + } + return (0); + } + db = open_module (); + + + /* If we've specified something like "cvs co foo/bar baz/quux" + don't try to shorten names. There are a few cases in which we + could shorten (e.g. "cvs co foo/bar foo/baz"), but we don't + handle those yet. Better to have an extra directory created + than the thing checked out under the wrong directory name. */ + + if (argc > 1) + shorten = 0; + + + /* If we will be calling history_write, work out the name to pass + it. */ + if (!pipeout) + { + if (!date) + history_name = tag; + else if (!tag) + history_name = date; + else + { + history_name = xmalloc (strlen (tag) + strlen (date) + 2); + sprintf (history_name, "%s:%s", tag, date); + } + } + + + for (i = 0; i < argc; i++) + err += do_module (db, argv[i], m_type, "Updating", checkout_proc, + where, shorten, local, run_module_prog, !pipeout, + (char *) NULL); + close_module (db); + if (options) + { + free (options); + options = NULL; + } + if (history_name != tag && history_name != date && history_name != NULL) + free (history_name); + return (err); +} + +/* FIXME: This is and emptydir_name are in checkout.c for historical + reasons, probably want to move them. */ + +/* int + * safe_location ( char *where ) + * + * Return true if where is a safe destination for a checkout. + * + * INPUTS + * where The requested destination directory. + * + * GLOBALS + * current_parsed_root->directory + * current_parsed_root->isremote + * Used to locate our CVSROOT. + * + * RETURNS + * true If we are running in client mode or if where is not located + * within the CVSROOT. + * false Otherwise. + * + * ERRORS + * Exits with a fatal error message when various events occur, such as not + * being able to resolve a path or failing ot chdir to a path. + */ +int +safe_location (char *where) +{ + char *current; + char *hardpath; + size_t hardpath_len; + int retval; + + TRACE( TRACE_FUNCTION, "safe_location( where=%s )", + where ? where : "(null)" ); + +#ifdef CLIENT_SUPPORT + /* Don't compare remote CVSROOTs to our destination directory. */ + if ( current_parsed_root->isremote ) return 1; +#endif /* CLIENT_SUPPORT */ + + /* set current - even if where is set we'll need to cd back... */ + current = xgetwd (); + if (current == NULL) + error (1, errno, "could not get working directory"); + + hardpath = xresolvepath ( current_parsed_root->directory ); + + /* if where is set, set current to as much of where as exists, + * or fail. + */ + if (where != NULL) + { + char *where_this_pass = xstrdup (where); + while (1) + { + if (CVS_CHDIR (where_this_pass) != -1) + { + /* where */ + free (where_this_pass); + where_this_pass = xgetwd(); + if (where_this_pass == NULL) + error (1, errno, "could not get working directory"); + + if (CVS_CHDIR (current) == -1) + error (1, errno, + "could not restore directory to `%s'", current); + + free (current); + current = where_this_pass; + break; + } + else if (errno == ENOENT) + { + /* where_this_pass - last_component (where_this_pass) */ + char *parent; + + /* It's okay to cast out the const below since we know we + * allocated where_this_pass and have control of it. + */ + if ((parent = (char *)last_component (where_this_pass)) + != where_this_pass) + { + /* strip the last_component */ + parent[-1] = '\0'; + /* continue */ + } + else + { + /* ERRNO == ENOENT + * && last_component (where_this_pass) == where_this_pass + * means we've tried all the parent diretories and not one + * exists, so there is no need to test any portion of where + * - it is all being created. + */ + free (where_this_pass); + break; + } + } + else + /* we don't know how to handle other errors, so fail */ + error (1, errno, "\ +could not change directory to requested checkout directory `%s'", + where_this_pass); + } /* while (1) */ + } /* where != NULL */ + + hardpath_len = strlen (hardpath); + if (strlen (current) >= hardpath_len + && strncmp (current, hardpath, hardpath_len) == 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); + free( hardpath ); + 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 (struct dir_to_build *list, + int sticky); + +static void build_one_dir (char *, char *, int); + +static void +build_one_dir (char *repository, char *dirpath, int sticky) +{ + FILE *fp; + + if (isfile (CVSADM)) + { + if (m_type == EXPORT) + error (1, 0, "cannot export into a working directory"); + } + else if (m_type == CHECKOUT) + { + /* I suspect that this check could be omitted. */ + if (!isdir (repository)) + error (1, 0, "there is no repository %s", repository); + + if (Create_Admin (".", dirpath, repository, + sticky ? tag : (char *) NULL, + sticky ? date : (char *) NULL, + + /* 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, 1, 1)) + return; + + 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 + } + } +} + +/* + * process_module calls us back here so we do the actual checkout stuff + */ +/* ARGSUSED */ +static int +checkout_proc (int argc, char **argv, char *where_orig, char *mwhere, char *mfile, int shorten, int local_specified, char *omodule, char *msg) +{ + char *myargv[2]; + int err = 0; + int which; + char *cp; + char *repository; + char *oldupdate = NULL; + char *where; + + if (trace) + (void) fprintf (stderr, + "%s-> checkout_proc (%s, %s, %s, %d, %d, %s, %s)\n", + CLIENT_SERVER_STR, + where_orig ? where_orig : "(null)", + mwhere ? mwhere : "(null)", + mfile ? mfile : "(null)", + shorten, local_specified, + omodule ? omodule : "(null)", + msg ? msg : "(null)" + ); + + /* + * OK, so we're doing the checkout! Our args are as follows: + * argc,argv contain either dir or dir followed by a list of files + * where contains where to put it (if supplied by checkout) + * mwhere contains the module name or -d from module file + * mfile says do only that part of the module + * shorten = 1 says shorten as much as possible + * omodule is the original arg to do_module() + */ + + /* Set up the repository (maybe) for the bottom directory. + Allocate more space than we need so we don't need to keep + reallocating this string. */ + repository = xmalloc (strlen (current_parsed_root->directory) + + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile)) + + 10); + (void) sprintf (repository, "%s/%s", + current_parsed_root->directory, argv[0]); + Sanitize_Repository_Name (repository); + + + /* save the original value of preload_update_dir */ + if (preload_update_dir != NULL) + oldupdate = xstrdup (preload_update_dir); + + + /* Allocate space and set up the where variable. We allocate more + space than necessary here so that we don't have to keep + reallocaing it later on. */ + + where = xmalloc (strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile)) + + (mwhere == NULL ? 0 : strlen (mwhere)) + + (where_orig == NULL ? 0 : strlen (where_orig)) + + 10); + + /* Yes, this could be written in a less verbose way, but in this + form it is quite easy to read. + + FIXME? The following code that sets should probably be moved + to do_module in modules.c, since there is similar code in + patch.c and rtag.c. */ + + if (shorten) + { + if (where_orig != NULL) + { + /* If the user has specified a directory with `-d' on the + command line, use it preferentially, even over the `-d' + flag in the modules file. */ + + (void) strcpy (where, where_orig); + } + else if (mwhere != NULL) + { + /* Second preference is the value of mwhere, which is from + the `-d' flag in the modules file. */ + + (void) strcpy (where, mwhere); + } + else + { + /* Third preference is the directory specified in argv[0] + which is this module'e directory in the repository. */ + + (void) strcpy (where, argv[0]); + } + } + else + { + /* Use the same preferences here, bug don't shorten -- that + is, tack on where_orig if it exists. */ + + *where = '\0'; + + if (where_orig != NULL) + { + (void) strcat (where, where_orig); + (void) strcat (where, "/"); + } + + /* If the -d flag in the modules file specified an absolute + directory, let the user override it with the command-line + -d option. */ + + if ((mwhere != NULL) && (! isabsolute (mwhere))) + (void) strcat (where, mwhere); + else + (void) strcat (where, argv[0]); + } + strip_trailing_slashes (where); /* necessary? */ + + + /* At this point, the user may have asked for a single file or + directory from within a module. In that case, we should modify + where, repository, and argv as appropriate. */ + + if (mfile != NULL) + { + /* The mfile variable can have one or more path elements. If + it has multiple elements, we want to tack those onto both + repository and where. The last element may refer to either + a file or directory. Here's what to do: + + it refers to a directory + -> simply tack it on to where and repository + it refers to a file + -> munge argv to contain `basename mfile` */ + + char *cp; + char *path; + + + /* Paranoia check. */ + + if (mfile[strlen (mfile) - 1] == '/') + { + error (0, 0, "checkout_proc: trailing slash on mfile (%s)!", + mfile); + } + + + /* Does mfile have multiple path elements? */ + + cp = strrchr (mfile, '/'); + if (cp != NULL) + { + *cp = '\0'; + (void) strcat (repository, "/"); + (void) strcat (repository, mfile); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + mfile = cp + 1; + } + + + /* Now mfile is a single path element. */ + + path = xmalloc (strlen (repository) + strlen (mfile) + 5); + (void) sprintf (path, "%s/%s", repository, mfile); + if (isdir (path)) + { + /* It's a directory, so tack it on to repository and + where, as we did above. */ + + (void) strcat (repository, "/"); + (void) strcat (repository, mfile); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + } + else + { + /* It's a file, which means we have to screw around with + argv. */ + myargv[0] = argv[0]; + myargv[1] = mfile; + argc = 2; + argv = myargv; + } + free (path); + } + + if (preload_update_dir != NULL) + { + 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); + + /* + * At this point, where is the directory we want to build, repository is + * the repository for the lowest level of the path. + * + * 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. + */ + + /* + * If we are sending everything to stdout, we can skip a whole bunch of + * work from here + */ + if (!pipeout) + { + struct dir_to_build *head; + char *reposcopy; + + if (strncmp (repository, current_parsed_root->directory, + strlen (current_parsed_root->directory)) != 0) + error (1, 0, "\ +internal error: %s doesn't start with %s in checkout_proc", + repository, current_parsed_root->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; + + /* Make a copy of the repository name to play with. */ + reposcopy = xstrdup (repository); + + /* FIXME: this should be written in terms of last_component + instead of hardcoding '/'. This presumably affects OS/2, + NT, &c, if the user specifies '\'. Likewise for the call + to findslash. */ + cp = where + strlen (where); + while (cp > where) + { + struct dir_to_build *new; + + cp = findslash (where, cp - 1); + if (cp == NULL) + break; /* we're done */ + + new = (struct dir_to_build *) + xmalloc (sizeof (struct dir_to_build)); + new->dirpath = xmalloc (strlen (where)); + + /* If the user specified an absolute path for where, the + last path element we create should be the top-level + directory. */ + + if (cp > where) + { + strncpy (new->dirpath, where, cp - where); + new->dirpath[cp - where] = '\0'; + } + else + { + /* where should always be at least one character long. */ + assert (where[0] != '\0'); + strcpy (new->dirpath, "/"); + } + new->next = head; + head = new; + + /* Now figure out what repository directory to generate. + The most complete case would be something like this: + + The modules file contains + foo -d bar/baz quux + + The command issued was: + cvs co -d what/ever -N foo + + The results in the CVS/Repository files should be: + . -> (don't touch CVS/Repository) + (I think this case might be buggy currently) + what -> (don't touch CVS/Repository) + ever -> . (same as "cd what/ever; cvs co -N foo") + bar -> Emptydir (generated dir -- not in repos) + baz -> quux (finally!) */ + + if (strcmp (reposcopy, current_parsed_root->directory) == 0) + { + /* We can't walk up past CVSROOT. Instead, the + repository should be Emptydir. */ + new->repository = emptydir_name (); + } + else + { + /* It's a directory in the repository! */ + + char *rp; + + /* We'll always be below CVSROOT, but check for + paranoia's sake. */ + rp = strrchr (reposcopy, '/'); + if (rp == NULL) + error (1, 0, + "internal error: %s doesn't contain a slash", + reposcopy); + + *rp = '\0'; + new->repository = xmalloc (strlen (reposcopy) + 5); + (void) strcpy (new->repository, reposcopy); + + if (strcmp (reposcopy, current_parsed_root->directory) == 0) + { + /* Special case -- the repository name needs + to be "/path/to/repos/." (the trailing dot + is important). We might be able to get rid + of this after the we check out the other + code that handles repository names. */ + (void) strcat (new->repository, "/."); + } + } + } + + /* clean up */ + free (reposcopy); + + /* The top-level CVSADM directory should always be + current_parsed_root->directory. Create it, but only if WHERE is + relative. If WHERE is absolute, our current directory + may not have a thing to do with where the sources are + being checked out. If it does, build_dirs_and_chdir + will take care of creating adm files here. */ + /* FIXME: checking where_is_absolute is a horrid kludge; + I suspect we probably can just skip the call to + build_one_dir whenever the -d command option was specified + to checkout. */ + + if (!isabsolute (where) && top_level_admin && m_type == CHECKOUT) + { + /* It may be argued that we shouldn't set any sticky + bits for the top-level repository. FIXME? */ + build_one_dir (current_parsed_root->directory, ".", argc <= 1); + +#ifdef SERVER_SUPPORT + /* We _always_ want to have a top-level admin + directory. If we're running in client/server mode, + send a "Clear-static-directory" command to make + sure it is created on the client side. (See 5.10 + in cvsclient.dvi to convince yourself that this is + OK.) If this is a duplicate command being sent, it + will be ignored on the client side. */ + + if (server_active) + server_clear_entstat (".", current_parsed_root->directory); +#endif + } + + + /* Build dirs on the path if necessary and leave us in the + bottom directory (where if where was specified) doesn't + contain a CVS subdir yet, but all the others contain + CVS and Entries.Static files */ + + if (build_dirs_and_chdir (head, argc <= 1) != 0) + { + error (0, 0, "ignoring module %s", omodule); + err = 1; + goto out; + } + + /* set up the repository (or make sure the old one matches) */ + if (!isfile (CVSADM)) + { + FILE *fp; + + if (!noexec && argc > 1) + { + /* I'm not sure whether this check is redundant. */ + if (!isdir (repository)) + error (1, 0, "there is no repository %s", repository); + + Create_Admin (".", preload_update_dir, repository, + (char *) NULL, (char *) NULL, 0, 0, + m_type == CHECKOUT); + 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 (where, repository); +#endif + } + else + { + /* I'm not sure whether this check is redundant. */ + if (!isdir (repository)) + error (1, 0, "there is no repository %s", repository); + + 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, 0, m_type == CHECKOUT); + } + } + else + { + char *repos; + + if (m_type == EXPORT) + error (1, 0, "cannot export into working directory"); + + /* get the contents of the previously existing repository */ + repos = Name_Repository ((char *) NULL, preload_update_dir); + if (fncmp (repository, repos) != 0) + { + error (0, 0, "existing repository %s does not match %s", + repos, repository); + error (0, 0, "ignoring module %s", omodule); + free (repos); + err = 1; + goto out; + } + free (repos); + } + } + + /* + * If we are going to be updating to stdout, we need to cd to the + * repository directory so the recursion processor can use the current + * directory as the place to find repository information + */ + if (pipeout) + { + if ( CVS_CHDIR (repository) < 0) + { + error (0, errno, "cannot chdir to %s", repository); + err = 1; + goto out; + } + which = W_REPOS; + if (tag != NULL && !tag_validated) + { + tag_check_valid (tag, argc - 1, argv + 1, 0, aflag, NULL); + tag_validated = 1; + } + } + else + { + which = W_LOCAL | W_REPOS; + if (tag != NULL && !tag_validated) + { + tag_check_valid (tag, argc - 1, argv + 1, 0, aflag, + repository); + tag_validated = 1; + } + } + + if (tag != NULL || date != NULL || join_rev1 != NULL) + which |= W_ATTIC; + + if (! join_tags_validated) + { + if (join_rev1 != NULL) + tag_check_valid_join (join_rev1, argc - 1, argv + 1, 0, aflag, + repository); + if (join_rev2 != NULL) + tag_check_valid_join (join_rev2, argc - 1, argv + 1, 0, aflag, + repository); + join_tags_validated = 1; + } + + /* + * if we are going to be recursive (building dirs), go ahead and call the + * update recursion processor. We will be recursive unless either local + * only was specified, or we were passed arguments + */ + if (!(local_specified || argc > 1)) + { + if (!pipeout) + history_write (m_type == CHECKOUT ? 'O' : 'E', preload_update_dir, + history_name, 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, m_type == CHECKOUT, + repository ); + goto out; + } + + if (!pipeout) + { + int i; + List *entries; + + /* we are only doing files, so register them */ + entries = Entries_Open (0, NULL); + for (i = 1; i < argc; i++) + { + char *line; + Vers_TS *vers; + 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 (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); + } + + /* Don't log "export", just regular "checkouts" */ + if (m_type == CHECKOUT && !pipeout) + history_write ('O', preload_update_dir, history_name, where, + repository); + + /* go ahead and call update now that everything is set */ + err += do_update ( argc - 1, argv + 1, options, tag, date, + force_tag_match, local_specified, 1 /* update -d */, + aflag, checkout_prune_dirs, pipeout, which, join_rev1, + join_rev2, preload_update_dir, m_type == CHECKOUT, + repository ); +out: + free (preload_update_dir); + preload_update_dir = oldupdate; + free (where); + free (repository); + return (err); +} + +static char * +findslash (char *start, char *p) +{ + for (;;) + { + if (*p == '/') return p; + if (p == start) break; + --p; + } + return NULL; +} + +/* Return a newly malloc'd string containing a pathname for CVSNULLREPOS, + and make sure that it exists. If there is an error creating the + directory, give a fatal error. Otherwise, the directory is guaranteed + to exist when we return. */ +char * +emptydir_name (void) +{ + char *repository; + + repository = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + sizeof (CVSNULLREPOS) + + 3); + (void) sprintf (repository, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSNULLREPOS); + if (!isfile (repository)) + { + mode_t omask; + omask = umask (cvsumask); + if (CVS_MKDIR (repository, 0777) < 0) + error (1, errno, "cannot create %s", repository); + (void) umask (omask); + } + return repository; +} + +/* Build all the dirs along the path to DIRS with CVS subdirs with appropriate + * repositories. If DIRS->repository is NULL or the directory already exists, + * do not create a CVSADM directory for that subdirectory; just CVS_CHDIR into + * it. Frees all storage used by DIRS. + * + * ASSUMPTIONS + * 1. Parent directories will be listed in DIRS before their children. + * 2. At most a single directory will need to be changed at one time. In + * other words, if we are in /a/b/c, and our final destination is + * /a/b/c/d/e/f, then we will build d, then d/e, then d/e/f. + * + * INPUTS + * dirs Simple list composed of dir_to_build structures, listing + * information about directories to build. + * sticky Passed to build_one_dir to tell it whether there are any sticky + * tags or dates to be concerned with. + * + * RETURNS + * 1 on error, 0 otherwise. + * + * ERRORS + * The only nonfatal error this function may return is if the CHDIR fails. + */ +static int +build_dirs_and_chdir (struct dir_to_build *dirs, int sticky) +{ + int retval = 0; + struct dir_to_build *nextdir; + + while (dirs != NULL) + { + const char *dir = last_component (dirs->dirpath); + int made_dir = 0; + + made_dir = !mkdir_if_needed (dir); + if (made_dir) Subdir_Register (NULL, NULL, dir); + + if (CVS_CHDIR (dir) < 0) + { + error (0, errno, "cannot chdir to %s", dir); + retval = 1; + goto out; + } + if (dirs->repository != NULL) + { + if (made_dir) + build_one_dir (dirs->repository, dirs->dirpath, sticky); + free (dirs->repository); + } + nextdir = dirs->next; + free (dirs->dirpath); + free (dirs); + dirs = nextdir; + } + + out: + while (dirs != NULL) + { + if (dirs->repository != NULL) + free (dirs->repository); + nextdir = dirs->next; + free (dirs->dirpath); + free (dirs); + dirs = nextdir; + } + return retval; +} diff --git a/contrib/cvs-1.12.9/src/classify.c b/contrib/cvs-1.12.9/src/classify.c new file mode 100644 index 0000000000..726d54479e --- /dev/null +++ b/contrib/cvs-1.12.9/src/classify.c @@ -0,0 +1,429 @@ +/* + * 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 source distribution. + * + */ + +#include "cvs.h" + +static void sticky_ck (struct file_info *finfo, int aflag, + Vers_TS * vers); + +/* + * Classify the state of a file. + * + * INPUTS + * finfo Information about the file to be classified. + * tag + * date + * options Keyword expansion options. Can be either NULL or "" to + * indicate none are specified here. + * force_tag_match + * aflag + * versp + * pipeout Did the user pass the "pipeout" flag to request that + * all output go to STDOUT rather than to a file or files? + * + * RETURNS + * A Ctype (defined as an enum) describing the state of the file relative to + * the repository. See the definition of Ctype for more. + */ +Ctype +Classify_File (struct file_info *finfo, char *tag, char *date, char *options, + int force_tag_match, int aflag, Vers_TS **versp, int pipeout) +{ + Vers_TS *vers; + Ctype ret; + + /* get all kinds of good data about the file */ + vers = Version_TS (finfo, options, tag, date, + force_tag_match, 0); + + if (vers->vn_user == NULL) + { + /* No entry available, ts_rcs is invalid */ + if (vers->vn_rcs == NULL) + { + /* there is no RCS file either */ + if (vers->ts_user == NULL) + { + /* there is no user file */ + /* 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'", + 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 `%s add' to create an entry for `%s'", + program_name, finfo->fullname); + ret = T_UNKNOWN; + } + } + else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) + { + /* there is an RCS file, but it's dead */ + if (vers->ts_user == NULL) + ret = T_UPTODATE; + else + { + error (0, 0, "use `%s add' to create an entry for `%s'", + program_name, finfo->fullname); + ret = T_UNKNOWN; + } + } + else if (!pipeout && vers->ts_user && 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", + finfo->fullname); + ret = T_CONFLICT; + } + else + /* no user file or no difference, just checkout */ + ret = T_CHECKOUT; + } + else if (strcmp (vers->vn_user, "0") == 0) + { + /* An entry for a new-born file; ts_rcs is dummy */ + + if (vers->ts_user == NULL) + { + if (pipeout) + { + ret = T_CHECKOUT; + } + else + { + /* + * There is no user file, but there should be one; remove the + * entry + */ + if (!really_quiet) + error (0, 0, "warning: new-born `%s' has disappeared", + finfo->fullname); + ret = T_REMOVE_ENTRY; + } + } + else if (vers->vn_rcs == NULL || + RCS_isdead (vers->srcfile, vers->vn_rcs)) + /* No RCS file or RCS file revision is dead */ + ret = T_ADDED; + else + { + if (pipeout) + { + ret = T_CHECKOUT; + } + else + { + if (vers->srcfile->flags & INATTIC + && vers->srcfile->flags & VALID) + { + /* This file has been added on some branch other than + the one we are looking at. In the branch we are + looking at, the file was already valid. */ + if (!really_quiet) + error (0, 0, + "conflict: `%s' has been added, but already exists", + finfo->fullname); + } + else + { + /* + * There is an RCS file, so someone else must have checked + * one in behind our back; conflict + */ + if (!really_quiet) + error (0, 0, + "conflict: `%s' created independently by" + " second party", + finfo->fullname); + } + ret = T_CONFLICT; + } + } + } + else if (vers->vn_user[0] == '-') + { + /* An entry for a removed file, ts_rcs is invalid */ + + if (vers->ts_user == NULL) + { + /* There is no user file (as it should be) */ + + if (vers->vn_rcs == NULL + || RCS_isdead (vers->srcfile, vers->vn_rcs)) + { + + /* + * There is no RCS file; this is all-right, but it has been + * removed independently by a second party; remove the entry + */ + ret = T_REMOVE_ENTRY; + } + else if (strcmp (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 + */ + ret = T_REMOVED; + else if (pipeout) + /* + * The RCS file doesn't match the user's file, but it doesn't + * matter in this case + */ + ret = T_NEEDS_MERGE; + else + { + + /* + * The RCS file is a newer version than the removed user file + * and this is definitely not OK; make it a conflict. + */ + if (!really_quiet) + error (0, 0, + "conflict: removed `%s' was modified by" + " second party", + finfo->fullname); + ret = T_CONFLICT; + } + } + else + { + /* The user file shouldn't be there */ + if (!really_quiet) + error (0, 0, "`%s' should be removed and is still there", + finfo->fullname); + ret = T_REMOVED; + } + } + else + { + /* A normal entry, TS_Rcs is valid */ + if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) + { + /* There is no RCS file */ + + if (vers->ts_user == NULL) + { + /* There is no user file, so just remove the entry */ + if (!really_quiet) + error (0, 0, "warning: `%s' is not (any longer) pertinent", + finfo->fullname); + ret = T_REMOVE_ENTRY; + } + else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) + { + + /* + * The user file is still unmodified, so just remove it from + * the entry list + */ + if (!really_quiet) + error (0, 0, "`%s' is no longer in the repository", + finfo->fullname); + ret = T_REMOVE_ENTRY; + } + else 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", + finfo->fullname); + ret = T_CONFLICT; + } + else + { + /* they weren't really different */ + if (!really_quiet) + error (0, 0, + "warning: `%s' is not (any longer) pertinent", + finfo->fullname); + ret = T_REMOVE_ENTRY; + } + } + else if (strcmp (vers->vn_rcs, vers->vn_user) == 0) + { + /* The RCS file is the same version as the user file */ + + if (vers->ts_user == NULL) + { + + /* + * There is no user file, so note that it was lost and + * extract a new version + */ + /* Comparing the cvs_cmd_name against "update", in + addition to being an ugly way to operate, means + that this message does not get printed by the + server. That might be considered just a straight + bug, although there is one subtlety: that case also + gets hit when a patch fails and the client fetches + a file. I'm not sure there is currently any way + for the server to distinguish those two cases. */ + if (strcmp (cvs_cmd_name, "update") == 0) + if (!really_quiet) + error (0, 0, "warning: `%s' was lost", finfo->fullname); + ret = T_CHECKOUT; + } + else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) + { + + /* + * The user file is still unmodified, so nothing special at + * all to do -- no lists updated, unless the sticky -k option + * has changed. If the sticky tag has changed, we just need + * to re-register the entry + */ + /* TODO: decide whether we need to check file permissions + for a mismatch, and return T_CONFLICT if so. */ + if (vers->entdata->options && + strcmp (vers->entdata->options, vers->options) != 0) + ret = T_CHECKOUT; + else + { + sticky_ck (finfo, aflag, vers); + ret = T_UPTODATE; + } + } + else if (No_Difference (finfo, vers)) + { + + /* + * they really are different; modified if we aren't + * changing any sticky -k options, else needs merge + */ +#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED + if (strcmp (vers->entdata->options ? + vers->entdata->options : "", vers->options) == 0) + ret = T_MODIFIED; + else + ret = T_NEEDS_MERGE; +#else + ret = T_MODIFIED; + sticky_ck (finfo, aflag, vers); +#endif + } + else if (strcmp (vers->entdata->options ? + vers->entdata->options : "", vers->options) != 0) + { + /* file has not changed; check out if -k changed */ + ret = T_CHECKOUT; + } + else + { + + /* + * else -> note that No_Difference will Register the + * file already for us, using the new tag/date. This + * is the desired behaviour + */ + ret = T_UPTODATE; + } + } + else + { + /* The RCS file is a newer version than the user file */ + + if (vers->ts_user == NULL) + { + /* There is no user file, so just get it */ + + /* See comment at other "update" compare, for more + thoughts on this comparison. */ + if (strcmp (cvs_cmd_name, "update") == 0) + if (!really_quiet) + error (0, 0, "warning: `%s' was lost", finfo->fullname); + ret = T_CHECKOUT; + } + else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) + { + + /* + * The user file is still unmodified, so just get it as well + */ + if (strcmp (vers->entdata->options ? + vers->entdata->options : "", vers->options) != 0 + || (vers->srcfile != NULL + && (vers->srcfile->flags & INATTIC) != 0)) + ret = T_CHECKOUT; + else + ret = T_PATCH; + } + else if (No_Difference (finfo, vers)) + /* really modified, needs to merge */ + ret = T_NEEDS_MERGE; + else if ((strcmp (vers->entdata->options ? + vers->entdata->options : "", vers->options) + != 0) + || (vers->srcfile != NULL + && (vers->srcfile->flags & INATTIC) != 0)) + /* not really modified, check it out */ + ret = T_CHECKOUT; + else + ret = T_PATCH; + } + } + + /* free up the vers struct, or just return it */ + if (versp != (Vers_TS **) NULL) + *versp = vers; + else + freevers_ts (&vers); + + /* return the status of the file */ + return (ret); +} + +static void +sticky_ck (struct file_info *finfo, int aflag, Vers_TS *vers) +{ + if (aflag || vers->tag || vers->date) + { + char *enttag = vers->entdata->tag; + char *entdate = vers->entdata->date; + + if ((enttag && vers->tag && strcmp (enttag, vers->tag)) || + ((enttag && !vers->tag) || (!enttag && vers->tag)) || + (entdate && vers->date && strcmp (entdate, vers->date)) || + ((entdate && !vers->date) || (!entdate && vers->date))) + { + Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs, + vers->options, vers->tag, vers->date, vers->ts_conflict); + +#ifdef SERVER_SUPPORT + if (server_active) + { + /* We need to update the entries line on the client side. + It is possible we will later update it again via + server_updated or some such, but that is OK. */ + server_update_entries + (finfo->file, finfo->update_dir, finfo->repository, + strcmp (vers->ts_rcs, vers->ts_user) == 0 ? + SERVER_UPDATED : SERVER_MERGED); + } +#endif + } + } +} diff --git a/contrib/cvs-1.12.9/src/client.c b/contrib/cvs-1.12.9/src/client.c new file mode 100644 index 0000000000..cc95e075ad --- /dev/null +++ b/contrib/cvs-1.12.9/src/client.c @@ -0,0 +1,4944 @@ +/* CVS client-related stuff. + + 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. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "cvs.h" +#include "getline.h" +#include "edit.h" +#include "buffer.h" +#include "log-buffer.h" +#include "savecwd.h" + +#ifdef CLIENT_SUPPORT + +# include "md5.h" + +#include "socket-client.h" +#include "rsh-client.h" + +# ifdef HAVE_GSSAPI +# include "gssapi-client.h" +# endif + +# ifdef HAVE_KERBEROS +# include "kerberos4-client.h" +# endif + + + +/* Keep track of any paths we are sending for Max-dotdot so that we can verify + * that uplevel paths coming back form the server are valid. + * + * FIXME: The correct way to do this is probably provide some sort of virtual + * path map on the client side. This would be generic enough to be applied to + * absolute paths supplied by the user too. + */ +static List *uppaths = NULL; + + + +static void add_prune_candidate (const char *); + +/* All the commands. */ +int add (int argc, char **argv); +int admin (int argc, char **argv); +int checkout (int argc, char **argv); +int commit (int argc, char **argv); +int diff (int argc, char **argv); +int history (int argc, char **argv); +int import (int argc, char **argv); +int cvslog (int argc, char **argv); +int patch (int argc, char **argv); +int release (int argc, char **argv); +int cvsremove (int argc, char **argv); +int rtag (int argc, char **argv); +int status (int argc, char **argv); +int tag (int argc, char **argv); +int update (int argc, char **argv); + +/* All the response handling functions. */ +static void handle_ok (char *, int); +static void handle_error (char *, int); +static void handle_valid_requests (char *, int); +static void handle_checked_in (char *, int); +static void handle_new_entry (char *, int); +static void handle_checksum (char *, int); +static void handle_copy_file (char *, int); +static void handle_updated (char *, int); +static void handle_merged (char *, int); +static void handle_patched (char *, int); +static void handle_rcs_diff (char *, int); +static void handle_removed (char *, int); +static void handle_remove_entry (char *, int); +static void handle_set_static_directory (char *, int); +static void handle_clear_static_directory (char *, int); +static void handle_set_sticky (char *, int); +static void handle_clear_sticky (char *, int); +static void handle_clear_template (char *, int); +static void handle_module_expansion (char *, int); +static void handle_wrapper_rcs_option (char *, int); +static void handle_m (char *, int); +static void handle_e (char *, int); +static void handle_f (char *, int); +static void handle_notified (char *, int); + +static size_t try_read_from_server (char *, size_t); + +static void auth_server (cvsroot_t *, struct buffer *, struct buffer *, + int, int, struct hostent *); + +/* We need to keep track of the list of directories we've sent to the + server. This list, along with the current CVSROOT, will help us + decide which command-line arguments to send. */ +List *dirs_sent_to_server = NULL; + +static int is_arg_a_parent_or_listed_dir (Node *, void *); + +static int +is_arg_a_parent_or_listed_dir( Node *n, void *d ) +{ + char *directory = n->key; /* name of the dir sent to server */ + char *this_argv_elem = (char *) d; /* this argv element */ + + /* Say we should send this argument if the argument matches the + beginning of a directory name sent to the server. This way, + the server will know to start at the top of that directory + hierarchy and descend. */ + + if (strncmp (directory, this_argv_elem, strlen (this_argv_elem)) == 0) + return 1; + + return 0; +} + +static int arg_should_not_be_sent_to_server (char *); + +/* Return nonzero if this argument should not be sent to the + server. */ + +static int +arg_should_not_be_sent_to_server( char *arg ) +{ + /* Decide if we should send this directory name to the server. We + should always send argv[i] if: + + 1) the list of directories sent to the server is empty (as it + will be for checkout, etc.). + + 2) the argument is "." + + 3) the argument is a file in the cwd and the cwd is checked out + from the current root + + 4) the argument lies within one of the paths in + dirs_sent_to_server. + + */ + + if (list_isempty (dirs_sent_to_server)) + return 0; /* always send it */ + + if (strcmp (arg, ".") == 0) + return 0; /* always send it */ + + /* We should send arg if it is one of the directories sent to the + server or the parent of one; this tells the server to descend + the hierarchy starting at this level. */ + if (isdir (arg)) + { + if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir, arg)) + return 0; + + /* If arg wasn't a parent, we don't know anything about it (we + would have seen something related to it during the + send_files phase). Don't send it. */ + return 1; + } + + /* Try to decide whether we should send arg to the server by + checking the contents of the corresponding CVSADM directory. */ + { + char *t, *this_root; + + /* Calculate "dirname arg" */ + for (t = arg + strlen (arg) - 1; t >= arg; t--) + { + if (ISSLASH (*t)) + break; + } + + /* Now we're either poiting to the beginning of the + string, or we found a path separator. */ + if (t >= arg) + { + /* Found a path separator. */ + char c = *t; + *t = '\0'; + + /* First, check to see if we sent this directory to the + server, because it takes less time than actually + opening the stuff in the CVSADM directory. */ + if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir, + arg)) + { + *t = c; /* make sure to un-truncate the arg */ + return 0; + } + + /* Since we didn't find it in the list, check the CVSADM + files on disk. */ + this_root = Name_Root (arg, (char *) NULL); + *t = c; + } + else + { + /* We're at the beginning of the string. Look at the + CVSADM files in cwd. */ + this_root = (CVSroot_cmdline ? xstrdup(CVSroot_cmdline) + : Name_Root ((char *) NULL, (char *) NULL)); + } + + /* Now check the value for root. */ + if (this_root && current_parsed_root + && (strcmp (this_root, current_parsed_root->original) != 0)) + { + /* Don't send this, since the CVSROOTs don't match. */ + free (this_root); + return 1; + } + free (this_root); + } + + /* OK, let's send it. */ + return 0; +} + + + +#endif /* CLIENT_SUPPORT */ + + + +#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT) + +/* Shared with server. */ + +/* + * Return a malloc'd, '\0'-terminated string + * corresponding to the mode in SB. + */ +char * +mode_to_string (mode_t mode) +{ + 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); +} + +/* + * Change mode of FILENAME to MODE_STRING. + * Returns 0 for success or errno code. + * If RESPECT_UMASK is set, then honor the umask. + */ +int +change_mode( char *filename, char *mode_string, int respect_umask ) +{ +#ifdef CHMOD_BROKEN + char *p; + int writeable = 0; + + /* We can only distinguish between + 1) readable + 2) writeable + 3) Picasso's "Blue Period" + We handle the first two. */ + p = mode_string; + while (*p != '\0') + { + if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=') + { + char *q = p + 2; + while (*q != ',' && *q != '\0') + { + if (*q == 'w') + writeable = 1; + ++q; + } + } + /* Skip to the next field. */ + while (*p != ',' && *p != '\0') + ++p; + if (*p == ',') + ++p; + } + + /* xchmod honors the umask for us. In the !respect_umask case, we + don't try to cope with it (probably to handle that well, the server + needs to deal with modes in data structures, rather than via the + modes in temporary files). */ + xchmod (filename, writeable); + return 0; + +#else /* ! CHMOD_BROKEN */ + + char *p; + mode_t mode = 0; + mode_t oumask; + + p = mode_string; + while (*p != '\0') + { + if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=') + { + int can_read = 0, can_write = 0, can_execute = 0; + char *q = p + 2; + while (*q != ',' && *q != '\0') + { + if (*q == 'r') + can_read = 1; + else if (*q == 'w') + can_write = 1; + else if (*q == 'x') + can_execute = 1; + ++q; + } + if (p[0] == 'u') + { + if (can_read) + mode |= S_IRUSR; + if (can_write) + mode |= S_IWUSR; + if (can_execute) + mode |= S_IXUSR; + } + else if (p[0] == 'g') + { + if (can_read) + mode |= S_IRGRP; + if (can_write) + mode |= S_IWGRP; + if (can_execute) + mode |= S_IXGRP; + } + else if (p[0] == 'o') + { + if (can_read) + mode |= S_IROTH; + if (can_write) + mode |= S_IWOTH; + if (can_execute) + mode |= S_IXOTH; + } + } + /* Skip to the next field. */ + while (*p != ',' && *p != '\0') + ++p; + if (*p == ',') + ++p; + } + + if (respect_umask) + { + oumask = umask (0); + (void) umask (oumask); + mode &= ~oumask; + } + + if (chmod (filename, mode) < 0) + return errno; + return 0; +#endif /* ! CHMOD_BROKEN */ +} + +#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */ + +#ifdef CLIENT_SUPPORT + +int client_prune_dirs; + +static List *ignlist = (List *) NULL; + +/* Buffer to write to the server. */ +static struct buffer *global_to_server; + +/* Buffer used to read from the server. */ +static struct buffer *global_from_server; + + + +/* + * 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_via (struct buffer *via_from_buffer, struct buffer *via_to_buffer, + char **resultp) +{ + int status; + char *result; + int len; + + status = buf_flush (via_to_buffer, 1); + if (status != 0) + error (1, status, "writing to server"); + + status = buf_read_line (via_from_buffer, &result, &len); + if (status != 0) + { + 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) + *resultp = result; + else + free (result); + + return len; +} + + + +static int +read_line (char **resultp) +{ + return read_line_via (global_from_server, global_to_server, resultp); +} + + +#endif /* CLIENT_SUPPORT */ + + +#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT) + +/* + * Zero if compression isn't supported or requested; non-zero to indicate + * a compression level to request from gzip. + */ +int gzip_level; + +/* + * Level of compression to use when running gzip on a single file. + */ +int file_gzip_level; + +#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */ + +#ifdef CLIENT_SUPPORT + +/* + * The Repository for the top level of this command (not necessarily + * the CVSROOT, just the current directory at the time we do it). + */ +static char *toplevel_repos = NULL; + +/* 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( char *args, int len ) +{ + return; +} + +static void +handle_error( char *args, int len ) +{ + int something_printed; + + /* + * First there is a symbolic error code followed by a space, which + * we ignore. + */ + char *p = strchr (args, ' '); + if (p == NULL) + { + error (0, 0, "invalid data from cvs server"); + return; + } + ++p; + + /* Next we print the text of the message from the server. We + probably should be prefixing it with "server error" or some + such, because if it is something like "Out of memory", the + current behavior doesn't say which machine is out of + memory. */ + + len -= p - args; + something_printed = 0; + for (; len > 0; --len) + { + something_printed = 1; + putc (*p++, stderr); + } + if (something_printed) + putc ('\n', stderr); +} + +static void +handle_valid_requests( char *args, int len ) +{ + char *p = args; + char *q; + struct request *rq; + do + { + q = strchr (p, ' '); + if (q != NULL) + *q++ = '\0'; + for (rq = requests; rq->name != NULL; ++rq) + { + if (strcmp (rq->name, p) == 0) + break; + } + if (rq->name == NULL) + /* + * It is a request we have never heard of (and thus never + * will want to use). So don't worry about it. + */ + ; + else + { + if (rq->flags & RQ_ENABLEME) + { + /* + * Server wants to know if we have this, to enable the + * feature. + */ + send_to_server (rq->name, 0); + send_to_server ("\012", 0); + } + else + rq->flags |= RQ_SUPPORTED; + } + p = q; + } while (q != NULL); + for (rq = requests; rq->name != NULL; ++rq) + { + if ((rq->flags & RQ_SUPPORTED) + || (rq->flags & RQ_ENABLEME)) + continue; + if (rq->flags & RQ_ESSENTIAL) + error (1, 0, "request `%s' not supported by server", rq->name); + } +} + + + +/* + * This is a proc for walklist(). It inverts the error return premise of + * walklist. + * + * RETURNS + * True If this path is prefixed by one of the paths in walklist and + * does not step above the prefix path. + * False Otherwise. + */ +static +int path_list_prefixed (Node *p, void *closure) +{ + const char *questionable = closure; + const char *prefix = p->key; + if (strncmp (prefix, questionable, strlen (prefix))) return 0; + questionable += strlen (prefix); + while (ISSLASH (*questionable)) questionable++; + if (*questionable == '\0') return 1; + return pathname_levels (questionable); +} + + + +/* + * Need to validate the client pathname. Disallowed paths include: + * + * 1. Absolute paths. + * 2. Pathnames that do not reference a specifically requested update + * directory. + * + * In case 2, we actually only check that the directory is under the uppermost + * directories mentioned on the command line. + * + * RETURNS + * True If the path is valid. + * False Otherwise. + */ +static +int is_valid_client_path (const char *pathname) +{ + /* 1. Absolute paths. */ + if (isabsolute (pathname)) return 0; + /* 2. No up-references in path. */ + if (pathname_levels (pathname) == 0) return 1; + /* 2. No Max-dotdot paths registered. */ + if (uppaths == NULL) return 0; + + return walklist (uppaths, path_list_prefixed, (void *)pathname); +} + + + +/* + * Do all the processing for PATHNAME, where pathname consists of the + * repository and the filename. The parameters we pass to FUNC are: + * DATA is just the DATA parameter which was passed to + * call_in_directory; ENT_LIST is a pointer to an entries list (which + * we manage the storage for); SHORT_PATHNAME is the pathname of the + * file relative to the (overall) directory in which the command is + * taking place; and FILENAME is the filename portion only of + * SHORT_PATHNAME. When we call FUNC, the curent directory points to + * the directory portion of SHORT_PATHNAME. */ + +static void +call_in_directory( char *pathname, + void (*func) ( char *_data, List *_ent_list, + char *_short_pathname, char *_filename ), + char *data ) +{ + /* This variable holds the result of Entries_Open. */ + List *last_entries = NULL; + char *dir_name; + char *filename; + /* 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; + + /* + * Do the whole descent in parallel for the repositories, so we + * know what to put in CVS/Repository files. I'm not sure the + * full hair is necessary since the server does a similar + * computation; I suspect that we only end up creating one + * directory at a time anyway. + * + * Also note that we must *only* worry about this stuff when we + * are creating directories; `cvs co foo/bar; cd foo/bar; cvs co + * CVSROOT; cvs update' is legitimate, but in this case + * foo/bar/CVSROOT/CVS/Repository is not a subdirectory of + * foo/bar/CVS/Repository. + */ + char *reposname; + char *short_repos; + char *reposdirname; + char *rdirp; + int reposdirname_absolute; + int newdir = 0; + + reposname = NULL; + read_line (&reposname); + assert (reposname != NULL); + + reposdirname_absolute = 0; + if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0) + { + reposdirname_absolute = 1; + short_repos = reposname; + } + else + { + short_repos = reposname + strlen (toplevel_repos) + 1; + if (short_repos[-1] != '/') + { + reposdirname_absolute = 1; + short_repos = reposname; + } + } + + /* Now that we have SHORT_REPOS, we can calculate the path to the file we + * are being requested to operate on. + */ + filename = strrchr (short_repos, '/'); + if (filename == NULL) + filename = short_repos; + else + ++filename; + + short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5); + strcpy (short_pathname, pathname); + strcat (short_pathname, filename); + + /* Now that we know the path to the file we were requested to operate on, + * we can verify that it is valid. + * + * For security reasons, if SHORT_PATHNAME is absolute or attempts to + * ascend outside of the current sanbbox, we abort. The server should not + * send us anything but relative paths which remain inside the sandbox + * here. Anything less means a trojan CVS server could create and edit + * arbitrary files on the client. + */ + if (!is_valid_client_path (short_pathname)) + { + error (0, 0, + "Server attempted to update a file via an invalid pathname:"); + error (1, 0, "`%s'.", short_pathname); + } + + reposdirname = xstrdup (short_repos); + p = strrchr (reposdirname, '/'); + if (p == NULL) + { + reposdirname = xrealloc (reposdirname, 2); + reposdirname[0] = '.'; reposdirname[1] = '\0'; + } + else + *p = '\0'; + + dir_name = xstrdup (pathname); + p = strrchr (dir_name, '/'); + if (p == NULL) + { + dir_name = xrealloc (dir_name, 2); + dir_name[0] = '.'; dir_name[1] = '\0'; + } + else + *p = '\0'; + if (client_prune_dirs) + add_prune_candidate (dir_name); + + if (toplevel_wd == NULL) + { + toplevel_wd = xgetwd (); + if (toplevel_wd == NULL) + error (1, errno, "could not get working directory"); + } + + if (CVS_CHDIR (toplevel_wd) < 0) + error (1, errno, "could not chdir to %s", toplevel_wd); + + /* 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). We only need to do this for the "." case, + since the server takes care of forcing this directory to be + created in all other cases. If we don't create CVSADM + here, the call to Entries_Open below will fail. FIXME: + perhaps this means that we should change our algorithm + below that calls Create_Admin instead of having this code + here? */ + 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 + && (strcmp (dir_name, ".") == 0) + && ! 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, 1, 1); + + free (repo); + } + + if (CVS_CHDIR (dir_name) < 0) + { + char *dir; + char *dirp; + + if (! existence_error (errno)) + 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; + + /* This algorithm makes nested directories one at a time + and create CVS administration files in them. For + example, we're checking out foo/bar/baz from the + repository: + + 1) create foo, point CVS/Repository to /foo + 2) .. foo/bar .. /foo/bar + 3) .. foo/bar/baz .. /foo/bar/baz + + As you can see, we're just stepping along DIR_NAME (with + DIRP) and REPOSDIRNAME (with RDIRP) respectively. + + We need to be careful when we are checking out a + module, however, since DIR_NAME and REPOSDIRNAME are not + going to be the same. Since modules will not have any + slashes in their names, we should watch the output of + STRCHR to decide whether or not we should use STRCHR on + the RDIRP. That is, if we're down to a module name, + don't keep picking apart the repository directory name. */ + + do + { + dirp = strchr (dirp, '/'); + if (dirp) + { + strncpy (dir, dir_name, dirp - dir_name); + dir[dirp - dir_name] = '\0'; + /* Skip the slash. */ + ++dirp; + if (rdirp == NULL) + /* 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, '/'); + } + 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 + should be down to a DIRP that has no slashes, + so it won't help/hurt to do another STRCHR call + on DIRP. It will definitely hurt, however, if + we're down to a module name, since a module + name can point to a nested directory (that is, + DIRP will still have slashes in it. Therefore, + we should set it to NULL so the routine below + copies the contents of REMOTEDIRNAME onto the + root repository directory (does this if rdirp + is set to NULL, because we used to do an extra + STRCHR call here). */ + + 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 (mkdir_if_needed (dir)) + { + /* It already existed, fine. Just keep going. */ + } + else if (strcmp (cvs_cmd_name, "export") == 0) + /* Don't create CVSADM directories if this is export. */ + ; + else + { + /* + * Put repository in CVS/Repository. For historical + * (pre-CVS/Root) reasons, this is an absolute pathname, + * but what really matters is the part of it which is + * relative to cvsroot. + */ + char *repo; + char *r, *b; + + repo = xmalloc (strlen (reposdirname) + + strlen (toplevel_repos) + + 80); + if (reposdirname_absolute) + r = repo; + else + { + strcpy (repo, toplevel_repos); + strcat (repo, "/"); + r = repo + strlen (repo); + } + + 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'; + } + else + strcpy (r, reposdirname); + + Create_Admin (dir, dir, repo, + (char *)NULL, (char *)NULL, 0, 0, 1); + 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) + { + /* Skip the slash. */ + ++rdirp; + } + + } while (dirp != NULL); + free (dir); + /* Now it better work. */ + if ( CVS_CHDIR (dir_name) < 0) + error (1, errno, "could not chdir to %s", dir_name); + } + else if (strcmp (cvs_cmd_name, "export") == 0) + /* Don't create CVSADM directories if this is export. */ + ; + else if (!isdir (CVSADM)) + { + /* + * Put repository in CVS/Repository. For historical + * (pre-CVS/Root) reasons, this is an absolute pathname, + * but what really matters is the part of it which is + * relative to cvsroot. + */ + char *repo; + + if (reposdirname_absolute) + repo = reposdirname; + else + { + repo = xmalloc (strlen (reposdirname) + + strlen (toplevel_repos) + + 10); + strcpy (repo, toplevel_repos); + strcat (repo, "/"); + strcat (repo, reposdirname); + } + + Create_Admin (".", ".", repo, (char *)NULL, (char *)NULL, 0, 1, 1); + if (repo != reposdirname) + free (repo); + } + + if (strcmp (cvs_cmd_name, "export") != 0) + { + last_entries = Entries_Open (0, dir_name); + + /* 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); + } + } + free (reposdirname); + (*func) (data, last_entries, short_pathname, filename); + if (last_entries != NULL) + Entries_Close (last_entries); + free (dir_name); + free (short_pathname); + free (reposname); +} + +static void +copy_a_file( char *data, List *ent_list, char *short_pathname, char *filename ) +{ + char *newname; +#ifdef USE_VMS_FILENAMES + char *p; +#endif + + read_line (&newname); + +#ifdef USE_VMS_FILENAMES + /* Mogrify the filename so VMS is happy with it. */ + for(p = newname; *p; p++) + if(*p == '.' || *p == '#') *p = '_'; +#endif + /* cvsclient.texi has said for a long time that newname must be in the + same directory. Wouldn't want a malicious or buggy server overwriting + ~/.profile, /etc/passwd, or anything like that. */ + if (last_component (newname) != newname) + error (1, 0, "protocol error: Copy-file tried to specify directory"); + + if (unlink_file (newname) && !existence_error (errno)) + error (0, errno, "unable to remove %s", newname); + copy_file (filename, newname); + free (newname); +} + +static void +handle_copy_file( char *args, int len ) +{ + call_in_directory (args, copy_a_file, (char *)NULL); +} + + +static void read_counted_file (char *, char *); + +/* Read from the server the count for the length of a file, then read + the contents of that file and write them to FILENAME. FULLNAME is + the name of the file for use in error messages. FIXME-someday: + extend this to deal with compressed files and make update_entries + use it. On error, gives a fatal error. */ +static void +read_counted_file( char *filename, char *fullname ) +{ + char *size_string; + size_t size; + char *buf; + + /* Pointers in buf to the place to put data which will be read, + and the data which needs to be written, respectively. */ + char *pread; + char *pwrite; + /* Number of bytes left to read and number of bytes in buf waiting to + be written, respectively. */ + size_t nread; + size_t nwrite; + + FILE *fp; + + read_line (&size_string); + if (size_string[0] == 'z') + error (1, 0, "\ +protocol error: compressed files not supported for that operation"); + /* FIXME: should be doing more error checking, probably. Like using + strtoul and making sure we used up the whole line. */ + size = atoi (size_string); + free (size_string); + + /* A more sophisticated implementation would use only a limited amount + of buffer space (8K perhaps), and read that much at a time. We allocate + a buffer for the whole file only to make it easy to keep track what + needs to be read and written. */ + buf = xmalloc (size); + + /* FIXME-someday: caller should pass in a flag saying whether it + is binary or not. I haven't carefully looked into whether + CVS/Template files should use local text file conventions or + not. */ + fp = CVS_FOPEN (filename, "wb"); + if (fp == NULL) + error (1, errno, "cannot write %s", fullname); + nread = size; + nwrite = 0; + pread = buf; + pwrite = buf; + while (nread > 0 || nwrite > 0) + { + size_t n; + + if (nread > 0) + { + n = try_read_from_server (pread, nread); + nread -= n; + pread += n; + nwrite += n; + } + + if (nwrite > 0) + { + n = fwrite (pwrite, 1, nwrite, fp); + if (ferror (fp)) + error (1, errno, "cannot write %s", fullname); + nwrite -= n; + pwrite += n; + } + } + free (buf); + if (fclose (fp) < 0) + error (1, errno, "cannot close %s", fullname); +} + +/* OK, we want to swallow the "U foo.c" response and then output it only + if we can update the file. In the future we probably want some more + systematic approach to parsing tagged text, but for now we keep it + ad hoc. "Why," I hear you cry, "do we not just look at the + Update-existing and Created responses?" That is an excellent question, + and the answer is roughly conservatism/laziness--I haven't read through + update.c enough to figure out the exact correspondence or lack thereof + between those responses and a "U foo.c" line (note that Merged, from + join_file, can be either "C foo" or "U foo" depending on the context). */ +/* Nonzero if we have seen +updated and not -updated. */ +static int updated_seen; +/* Filename from an "fname" tagged response within +updated/-updated. */ +static char *updated_fname; + +/* This struct is used to hold data when reading the +importmergecmd + and -importmergecmd tags. We put the variables in a struct only + for namespace issues. FIXME: As noted above, we need to develop a + more systematic approach. */ +static struct +{ + /* Nonzero if we have seen +importmergecmd and not -importmergecmd. */ + int seen; + /* Number of conflicts, from a "conflicts" tagged response. */ + int conflicts; + /* First merge tag, from a "mergetag1" tagged response. */ + char *mergetag1; + /* Second merge tag, from a "mergetag2" tagged response. */ + char *mergetag2; + /* Repository, from a "repository" tagged response. */ + char *repository; +} importmergecmd; + +/* Nonzero if we should arrange to return with a failure exit status. */ +static int failure_exit; + + +/* + * 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. + */ + +static int stored_checksum_valid; +static unsigned char stored_checksum[16]; + +static void +handle_checksum( char *args, int len ) +{ + char *s; + char buf[3]; + int i; + + if (stored_checksum_valid) + error (1, 0, "Checksum received before last one was used"); + + s = args; + buf[2] = '\0'; + for (i = 0; i < 16; i++) + { + char *bufend; + + buf[0] = *s++; + buf[1] = *s++; + stored_checksum[i] = (char) strtol (buf, &bufend, 16); + if (bufend != buf + 2) + break; + } + + if (i < 16 || *s != '\0') + error (1, 0, "Invalid Checksum response: `%s'", args); + + stored_checksum_valid = 1; +} + +/* Mode that we got in a "Mode" response (malloc'd), or NULL if none. */ +static char *stored_mode; + +static void handle_mode (char *, int); + +static void +handle_mode( char *args, int len ) +{ + if (stored_mode != NULL) + error (1, 0, "protocol error: duplicate Mode"); + stored_mode = xstrdup (args); +} + +/* Nonzero if time was specified in Mod-time. */ +static int stored_modtime_valid; +/* Time specified in Mod-time. */ +static time_t stored_modtime; + +static void handle_mod_time (char *, int); + +static void +handle_mod_time( char *args, int len ) +{ + if (stored_modtime_valid) + error (0, 0, "protocol error: duplicate Mod-time"); + stored_modtime = get_date (args, NULL); + if (stored_modtime == (time_t) -1) + error (0, 0, "protocol error: cannot parse date %s", args); + else + stored_modtime_valid = 1; +} + +/* + * If we receive a patch, but the patch program fails to apply it, we + * want to request the original file. We keep a list of files whose + * patches have failed. + */ + +char **failed_patches; +int failed_patches_count; + +struct update_entries_data +{ + enum { + /* + * We are just getting an Entries line; the local file is + * correct. + */ + UPDATE_ENTRIES_CHECKIN, + /* We are getting the file contents as well. */ + UPDATE_ENTRIES_UPDATE, + /* + * We are getting a patch against the existing local file, not + * an entire new file. + */ + 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. + */ + char *timestamp; +}; + +/* Update the Entries line for this file. */ +static void +update_entries( char *data_arg, List *ent_list, char *short_pathname, + char *filename ) +{ + char *entries_line; + struct update_entries_data *data = (struct update_entries_data *)data_arg; + + char *cp; + char *user; + char *vn; + /* Timestamp field. Always empty according to the protocol. */ + char *ts; + char *options = NULL; + char *tag = NULL; + char *date = NULL; + char *tag_or_date; + char *scratch_entries = NULL; + int bin; + +#ifdef UTIME_EXPECTS_WRITABLE + int change_it_back = 0; +#endif + + read_line (&entries_line); + + /* + * Parse the entries line. + */ + scratch_entries = xstrdup (entries_line); + + if (scratch_entries[0] != '/') + error (1, 0, "bad entries line `%s' from server", entries_line); + user = scratch_entries + 1; + if ((cp = strchr (user, '/')) == NULL) + error (1, 0, "bad entries line `%s' from server", entries_line); + *cp++ = '\0'; + vn = cp; + if ((cp = strchr (vn, '/')) == NULL) + error (1, 0, "bad entries line `%s' from server", entries_line); + *cp++ = '\0'; + + ts = cp; + if ((cp = strchr (ts, '/')) == NULL) + error (1, 0, "bad entries line `%s' from server", entries_line); + *cp++ = '\0'; + options = cp; + if ((cp = strchr (options, '/')) == NULL) + error (1, 0, "bad entries line `%s' from server", entries_line); + *cp++ = '\0'; + tag_or_date = cp; + + /* If a slash ends the tag_or_date, ignore everything after it. */ + cp = strchr (tag_or_date, '/'); + if (cp != NULL) + *cp = '\0'; + if (*tag_or_date == 'T') + tag = tag_or_date + 1; + else if (*tag_or_date == 'D') + date = tag_or_date + 1; + + /* Done parsing the entries line. */ + + if (data->contents == UPDATE_ENTRIES_UPDATE + || data->contents == UPDATE_ENTRIES_PATCH + || data->contents == UPDATE_ENTRIES_RCS_DIFF) + { + char *size_string; + char *mode_string; + int size; + char *buf; + char *temp_filename; + int use_gzip; + int patch_failed; + + read_line (&mode_string); + + read_line (&size_string); + if (size_string[0] == 'z') + { + use_gzip = 1; + size = atoi (size_string+1); + } + else + { + use_gzip = 0; + size = atoi (size_string); + } + free (size_string); + + /* Note that checking this separately from writing the file is + a race condition: if the existence or lack thereof of the + file changes between now and the actual 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, See 3 for + additional notes. (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. In this case and in case 2, the filename + (short_pathname) printed in the error message will likely _not_ + have the same case as seen by the user in a directory listing. + (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); + if (updated_fname != NULL) + { + cvs_output ("C ", 0); + cvs_output (updated_fname, 0); + cvs_output ("\n", 1); + } + failure_exit = 1; + + 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 (scratch_entries); + free (entries_line); + + /* The Mode, Mod-time, and Checksum responses should not carry + over to a subsequent Created (or whatever) response, even + in the error case. */ + if (stored_mode != NULL) + { + free (stored_mode); + stored_mode = NULL; + } + stored_modtime_valid = 0; + stored_checksum_valid = 0; + + if (updated_fname != NULL) + { + free (updated_fname); + updated_fname = NULL; + } + return; + } + + temp_filename = xmalloc (strlen (filename) + 80); +#ifdef USE_VMS_FILENAMES + /* A VMS rename of "blah.dat" to "foo" to implies a + destination of "foo.dat" which is unfortinate for CVS */ + sprintf (temp_filename, "%s_new_", filename); +#else +#ifdef _POSIX_NO_TRUNC + sprintf (temp_filename, ".new.%.9s", filename); +#else /* _POSIX_NO_TRUNC */ + 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 + instead of just LF. Format translation is done in the C + library I/O funtions. Here we tell them whether or not to + convert -- if this file is marked "binary" with the RCS -kb + flag, then we don't want to convert, else we do (because + CVS assumes text files by default). */ + + if (options) + bin = !(strcmp (options, "-kb")); + else + bin = 0; + + if (data->contents == UPDATE_ENTRIES_RCS_DIFF) + { + /* This is an RCS change text. We just hold the change + text in memory. */ + + if (use_gzip) + error (1, 0, + "server error: gzip invalid with RCS change text"); + + read_from_server (buf, size); + } + else + { + int fd; + + 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 (size > 0) + { + read_from_server (buf, size); + + if (use_gzip) + { + if (gunzip_and_write (fd, short_pathname, + (unsigned char *) buf, size)) + error (1, 0, "aborting due to compression error"); + } + else if (write (fd, buf, size) != size) + error (1, errno, "writing %s", short_pathname); + } + + if (close (fd) < 0) + error (1, errno, "writing %s", short_pathname); + } + + /* This is after we have read the file from the net (a change + from previous versions, where the server would send us + "M U foo.c" before Update-existing or whatever), but before + we finish writing the file (arguably a bug). The timing + affects a user who wants status info about how far we have + gotten, and also affects whether "U foo.c" appears in addition + to various error messages. */ + if (updated_fname != NULL) + { + cvs_output ("U ", 0); + cvs_output (updated_fname, 0); + cvs_output ("\n", 1); + free (updated_fname); + updated_fname = 0; + } + + patch_failed = 0; + + if (data->contents == UPDATE_ENTRIES_UPDATE) + { + rename_file (temp_filename, filename); + } + else if (data->contents == UPDATE_ENTRIES_PATCH) + { + /* You might think we could just leave Patched out of + Valid-responses and not get this response. However, if + memory serves, the CVS 1.9 server bases this on -u + (update-patches), and there is no way for us to send -u + or not based on whether the server supports "Rcs-diff". + + Fall back to transmitting entire files. */ + patch_failed = 1; + } + else + { + char *filebuf; + size_t filebufsize; + 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); + filebuf = NULL; + filebufsize = 0; + nread = 0; + + get_file (filename, short_pathname, bin ? FOPEN_BINARY_READ : "r", + &filebuf, &filebufsize, &nread); + /* 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 cvs_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. */ + cvs_MD5Init (&context); + cvs_MD5Update (&context, + (unsigned char *) patchedbuf, patchedlen); + cvs_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; + } + + if (! patch_failed) + { + FILE *e; + + 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 && ! patch_failed) + { + FILE *e; + struct cvs_MD5Context context; + unsigned char buf[8192]; + unsigned len; + unsigned char checksum[16]; + + /* + * Compute the MD5 checksum. This will normally only be + * used when receiving a patch, so we always compute it + * here on the final file, rather than on the received + * data. + * + * Note that if the file is a text file, we should read it + * here using text mode, so its lines will be terminated the same + * way they were transmitted. + */ + e = CVS_FOPEN (filename, "r"); + if (e == NULL) + error (1, errno, "could not open %s", short_pathname); + + cvs_MD5Init (&context); + while ((len = fread (buf, 1, sizeof buf, e)) != 0) + cvs_MD5Update (&context, buf, len); + if (ferror (e)) + error (1, errno, "could not read %s", short_pathname); + cvs_MD5Final (checksum, &context); + + fclose (e); + + stored_checksum_valid = 0; + + if (memcmp (checksum, stored_checksum, 16) != 0) + { + if (data->contents != UPDATE_ENTRIES_PATCH) + error (1, 0, "checksum failure on %s", + short_pathname); + + error (0, 0, + "checksum failure after patch to %s; will refetch", + short_pathname); + + 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); + free (scratch_entries); + free (entries_line); + + return; + } + + { + int status = change_mode (filename, mode_string, 1); + if (status != 0) + error (0, status, "cannot change mode of %s", short_pathname); + } + + free (mode_string); + free (buf); + } + + if (stored_mode != NULL) + { + change_mode (filename, stored_mode, 1); + free (stored_mode); + stored_mode = NULL; + } + + if (stored_modtime_valid) + { + struct utimbuf t; + + memset (&t, 0, sizeof (t)); + t.modtime = stored_modtime; + (void) time (&t.actime); + +#ifdef UTIME_EXPECTS_WRITABLE + if (!iswritable (filename)) + { + xchmod (filename, 1); + change_it_back = 1; + } +#endif /* UTIME_EXPECTS_WRITABLE */ + + if (utime (filename, &t) < 0) + error (0, errno, "cannot set time on %s", filename); + +#ifdef UTIME_EXPECTS_WRITABLE + if (change_it_back) + { + xchmod (filename, 0); + change_it_back = 0; + } +#endif /* UTIME_EXPECTS_WRITABLE */ + + stored_modtime_valid = 0; + } + + /* + * Process the entries line. Do this after we've written the file, + * since we need the timestamp. + */ + if (strcmp (cvs_cmd_name, "export") != 0) + { + 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); + else + file_timestamp = NULL; + + /* + * These special version numbers signify that it is not up to + * date. Create a dummy timestamp which will never compare + * equal to the timestamp of the file. + */ + if (vn[0] == '\0' || strcmp (vn, "0") == 0 || vn[0] == '-') + local_timestamp = "dummy timestamp"; + else if (local_timestamp == NULL) + { + local_timestamp = file_timestamp; + + /* Checking for cvs_cmd_name of "commit" doesn't seem like + the cleanest way to handle this, but it seem to roughly + parallel what the :local: code which calls + mark_up_to_date ends up amounting to. Some day, should + think more about what the Checked-in response means + vis-a-vis both Entries and Base and clarify + cvsclient.texi accordingly. */ + + if (!strcmp (cvs_cmd_name, "commit")) + mark_up_to_date (filename); + } + + Register (ent_list, filename, vn, local_timestamp, + options, tag, date, ts[0] == '+' ? file_timestamp : NULL); + + if (file_timestamp) + free (file_timestamp); + + } + free (scratch_entries); + free (entries_line); +} + +static void +handle_checked_in( char *args, int 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); +} + +static void +handle_new_entry( char *args, int 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); +} + +static void +handle_updated( char *args, int 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 (char *, int); + +static void +handle_created( 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 (char *, int); + +static void +handle_update_existing( 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); +} + +static void +handle_merged( char *args, int 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); +} + +static void +handle_patched( char *args, int 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( 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); +} + +static void +remove_entry( char *data, List *ent_list, char *short_pathname, + char *filename ) +{ + Scratch_Entry (ent_list, filename); +} + +static void +handle_remove_entry( char *args, int len ) +{ + call_in_directory (args, remove_entry, (char *)NULL); +} + +static void +remove_entry_and_file( char *data, List *ent_list, char *short_pathname, + 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); +} + +static void +handle_removed( char *args, int len ) +{ + call_in_directory (args, remove_entry_and_file, (char *)NULL); +} + +/* Is this the top level (directory containing CVSROOT)? */ +static int +is_cvsroot_level( char *pathname ) +{ + if (strcmp (toplevel_repos, current_parsed_root->directory) != 0) + return 0; + + return strchr (pathname, '/') == NULL; +} + +static void +set_static( char *data, List *ent_list, char *short_pathname, char *filename ) +{ + FILE *fp; + fp = open_file (CVSADM_ENTSTAT, "w+"); + if (fclose (fp) == EOF) + error (1, errno, "cannot close %s", CVSADM_ENTSTAT); +} + +static void +handle_set_static_directory( char *args, int len ) +{ + if (strcmp (cvs_cmd_name, "export") == 0) + { + /* Swallow the repository. */ + read_line (NULL); + return; + } + call_in_directory (args, set_static, (char *)NULL); +} + +static void +clear_static( char *data, List *ent_list, char *short_pathname, + char *filename ) +{ + if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno)) + error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT); +} + +static void +handle_clear_static_directory( char *pathname, int len ) +{ + if (strcmp (cvs_cmd_name, "export") == 0) + { + /* Swallow the repository. */ + read_line (NULL); + return; + } + + if (is_cvsroot_level (pathname)) + { + /* + * Top level (directory containing CVSROOT). This seems to normally + * lack a CVS directory, so don't try to create files in it. + */ + return; + } + call_in_directory (pathname, clear_static, (char *)NULL); +} + +static void +set_sticky( char *data, List *ent_list, char *short_pathname, char *filename ) +{ + char *tagspec; + FILE *f; + + read_line (&tagspec); + + /* FIXME-update-dir: error messages should include the directory. */ + f = CVS_FOPEN (CVSADM_TAG, "w+"); + if (f == NULL) + { + /* Making this non-fatal is a bit of a kludge (see dirs2 + in testsuite). A better solution would be to avoid having + the server tell us about a directory we shouldn't be doing + anything with anyway (e.g. by handling directory + addition/removal better). */ + error (0, errno, "cannot open %s", CVSADM_TAG); + free (tagspec); + return; + } + if (fprintf (f, "%s\n", tagspec) < 0) + error (1, errno, "writing %s", CVSADM_TAG); + if (fclose (f) == EOF) + error (1, errno, "closing %s", CVSADM_TAG); + free (tagspec); +} + +static void +handle_set_sticky( char *pathname, int len ) +{ + if (strcmp (cvs_cmd_name, "export") == 0) + { + /* Swallow the repository. */ + read_line (NULL); + /* Swallow the tag line. */ + read_line (NULL); + return; + } + if (is_cvsroot_level (pathname)) + { + /* + * Top level (directory containing CVSROOT). This seems to normally + * lack a CVS directory, so don't try to create files in it. + */ + + /* Swallow the repository. */ + read_line (NULL); + /* Swallow the tag line. */ + read_line (NULL); + return; + } + + call_in_directory (pathname, set_sticky, (char *)NULL); +} + +static void +clear_sticky( char *data, List *ent_list, char *short_pathname, + char *filename ) +{ + if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno)) + error (1, errno, "cannot remove %s", CVSADM_TAG); +} + +static void +handle_clear_sticky( char *pathname, int len ) +{ + if (strcmp (cvs_cmd_name, "export") == 0) + { + /* Swallow the repository. */ + read_line (NULL); + return; + } + + if (is_cvsroot_level (pathname)) + { + /* + * Top level (directory containing CVSROOT). This seems to normally + * lack a CVS directory, so don't try to create files in it. + */ + return; + } + + call_in_directory (pathname, clear_sticky, (char *)NULL); +} + + +static void template (char *, List *, char *, char *); + +static void +template( char *data, List *ent_list, char *short_pathname, char *filename ) +{ + char *buf = xmalloc ( strlen ( short_pathname ) + + strlen ( CVSADM_TEMPLATE ) + + 2 ); + sprintf ( buf, "%s/%s", short_pathname, CVSADM_TEMPLATE ); + read_counted_file ( CVSADM_TEMPLATE, buf ); + free ( buf ); +} + +static void handle_template (char *, int); + +static void +handle_template( char *pathname, int len ) +{ + call_in_directory (pathname, template, NULL); +} + +static void +clear_template( char *data, List *ent_list, char *short_pathname, + char *filename ) +{ + if (unlink_file (CVSADM_TEMPLATE) < 0 && ! existence_error (errno)) + error (1, errno, "cannot remove %s", CVSADM_TEMPLATE); +} + +static void +handle_clear_template( char *pathname, int len ) +{ + call_in_directory (pathname, clear_template, NULL); +} + + + +struct save_dir { + char *dir; + struct save_dir *next; +}; + +struct save_dir *prune_candidates; + +static void +add_prune_candidate (const char *dir) +{ + struct save_dir *p; + + 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); + p->next = prune_candidates; + prune_candidates = p; +} + +static void process_prune_candidates (void); + +static void +process_prune_candidates( void ) +{ + struct save_dir *p; + struct save_dir *q; + + 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 (isemptydir (p->dir, 1)) + { + 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. */ + +static char *last_repos; +static char *last_update_dir; + + + +static void +send_repository (const char *dir, const char *repos, const char *update_dir) +{ + char *adm_name; + + /* FIXME: this is probably not the best place to check; I wish I + * knew where in here's callers to really trap this bug. To + * reproduce the bug, just do this: + * + * mkdir junk + * cd junk + * cvs -d some_repos update foo + * + * Poof, CVS seg faults and dies! It's because it's trying to + * send a NULL string to the server but dies in send_to_server. + * That string was supposed to be the repository, but it doesn't + * get set because there's no CVSADM dir, and somehow it's not + * getting set from the -d argument either... ? + */ + if (repos == NULL) + { + /* Lame error. I want a real fix but can't stay up to track + this down right now. */ + error (1, 0, "no repository"); + } + + if (update_dir == NULL || update_dir[0] == '\0') + update_dir = "."; + + if (last_repos != NULL + && strcmp (repos, last_repos) == 0 + && last_update_dir != NULL + && strcmp (update_dir, last_update_dir) == 0) + /* We've already sent it. */ + return; + + if (client_prune_dirs) + add_prune_candidate (update_dir); + + /* Add a directory name to the list of those sent to the + server. */ + if (update_dir && (*update_dir != '\0') + && (strcmp (update_dir, ".") != 0) + && (findnode (dirs_sent_to_server, update_dir) == NULL)) + { + Node *n; + n = getnode (); + n->type = NT_UNKNOWN; + n->key = xstrdup (update_dir); + n->data = NULL; + + if (addnode (dirs_sent_to_server, n)) + error (1, 0, "cannot add directory %s to list", n->key); + } + + /* 80 is large enough for any of CVSADM_*. */ + adm_name = xmalloc (strlen (dir) + 80); + + send_to_server ("Directory ", 0); + { + /* Send the directory name. I know that this + sort of duplicates code elsewhere, but each + case seems slightly different... */ + char buf[1]; + const char *p = update_dir; + while (*p != '\0') + { + assert (*p != '\012'); + if (ISSLASH (*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'; + if (dir[0] != '\0') + { + strcat (adm_name, dir); + strcat (adm_name, "/"); + } + strcat (adm_name, CVSADM_ENTSTAT); + if (isreadable (adm_name)) + { + send_to_server ("Static-directory\012", 0); + } + } + if (supported_request ("Sticky")) + { + FILE *f; + if (dir[0] == '\0') + strcpy (adm_name, CVSADM_TAG); + else + sprintf (adm_name, "%s/%s", dir, CVSADM_TAG); + + f = CVS_FOPEN (adm_name, "r"); + if (f == NULL) + { + if (! existence_error (errno)) + error (1, errno, "reading %s", adm_name); + } + else + { + char line[80]; + char *nl = NULL; + send_to_server ("Sticky ", 0); + while (fgets (line, sizeof (line), f) != NULL) + { + send_to_server (line, 0); + nl = strchr (line, '\n'); + if (nl != NULL) + break; + } + if (nl == NULL) + send_to_server ("\012", 1); + if (fclose (f) == EOF) + error (0, errno, "closing %s", adm_name); + } + } + free (adm_name); + if (last_repos != NULL) + free (last_repos); + if (last_update_dir != NULL) + free (last_update_dir); + last_repos = xstrdup (repos); + last_update_dir = xstrdup (update_dir); +} + + + +/* Send a Repository line and set toplevel_repos. */ + +void +send_a_repository (const char *dir, const char *repository, + const char *update_dir_in) +{ + char *update_dir = xstrdup (update_dir_in); + + if (toplevel_repos == NULL && repository != NULL) + { + if (update_dir[0] == '\0' + || (update_dir[0] == '.' && update_dir[1] == '\0')) + toplevel_repos = xstrdup (repository); + else + { + /* + * Get the repository from a CVS/Repository file if update_dir + * is absolute. This is not correct in general, because + * the CVS/Repository file might not be the top-level one. + * This is for cases like "cvs update /foo/bar" (I'm not + * sure it matters what toplevel_repos we get, but it does + * matter that we don't hit the "internal error" code below). + */ + if (update_dir[0] == '/') + toplevel_repos = Name_Repository (update_dir, update_dir); + else + { + /* + * Guess the repository of that directory by looking at a + * subdirectory and removing as many pathname components + * as are in update_dir. I think that will always (or at + * least almost always) be 1. + * + * So this deals with directories which have been + * renamed, though it doesn't necessarily deal with + * directories which have been put inside other + * directories (and cvs invoked on the containing + * directory). I'm not sure the latter case needs to + * work. + * + * 21 Aug 1998: Well, Mr. Above-Comment-Writer, it + * does need to work after all. When we are using the + * client in a multi-cvsroot environment, it will be + * fairly common that we have the above case (e.g., + * cwd checked out from one repository but + * subdirectory checked out from another). We can't + * assume that by walking up a directory in our wd we + * necessarily walk up a directory in the repository. + */ + /* + * This gets toplevel_repos wrong for "cvs update ../foo" + * but I'm not sure toplevel_repos matters in that case. + */ + + int repository_len, update_dir_len; + + strip_trailing_slashes (update_dir); + + repository_len = strlen (repository); + update_dir_len = strlen (update_dir); + + /* Try to remove the path components in UPDATE_DIR + from REPOSITORY. If the path elements don't exist + in REPOSITORY, or the removal of those path + elements mean that we "step above" + current_parsed_root->directory, set toplevel_repos to + current_parsed_root->directory. */ + if ((repository_len > update_dir_len) + && (strcmp (repository + repository_len - update_dir_len, + update_dir) == 0) + /* TOPLEVEL_REPOS shouldn't be above current_parsed_root->directory */ + && ((size_t)(repository_len - update_dir_len) + > strlen (current_parsed_root->directory))) + { + /* The repository name contains UPDATE_DIR. Set + toplevel_repos to the repository name without + UPDATE_DIR. */ + + toplevel_repos = xmalloc (repository_len - update_dir_len); + /* Note that we don't copy the trailing '/'. */ + strncpy (toplevel_repos, repository, + repository_len - update_dir_len - 1); + toplevel_repos[repository_len - update_dir_len - 1] = '\0'; + } + else + { + toplevel_repos = xstrdup (current_parsed_root->directory); + } + } + } + } + + send_repository (dir, repository, update_dir); + free (update_dir); +} + + + +/* The "expanded" modules. */ +static int modules_count; +static int modules_allocated; +static char **modules_vector; + +static void +handle_module_expansion( char *args, int len ) +{ + if (modules_vector == NULL) + { + modules_allocated = 1; /* Small for testing */ + modules_vector = (char **) xmalloc + (modules_allocated * sizeof (modules_vector[0])); + } + else if (modules_count >= modules_allocated) + { + modules_allocated *= 2; + modules_vector = (char **) xrealloc + ((char *) modules_vector, + modules_allocated * sizeof (modules_vector[0])); + } + modules_vector[modules_count] = xmalloc (strlen (args) + 1); + strcpy (modules_vector[modules_count], args); + ++modules_count; +} + +/* Original, not "expanded" modules. */ +static int module_argc; +static char **module_argv; + +void +client_expand_modules( int argc, char **argv, int local ) +{ + int errs; + int i; + + module_argc = argc; + module_argv = (char **) xmalloc ((argc + 1) * sizeof (module_argv[0])); + for (i = 0; i < argc; ++i) + module_argv[i] = xstrdup (argv[i]); + module_argv[argc] = NULL; + + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + send_a_repository ("", current_parsed_root->directory, ""); + + send_to_server ("expand-modules\012", 0); + + errs = get_server_responses (); + if (last_repos != NULL) + free (last_repos); + last_repos = NULL; + if (last_update_dir != NULL) + free (last_update_dir); + last_update_dir = NULL; + if (errs) + error (errs, 0, "cannot expand modules"); +} + +void +client_send_expansions( int local, char *where, int build_dirs ) +{ + int i; + char *argv[1]; + + /* Send the original module names. The "expanded" module name might + not be suitable as an argument to a co request (e.g. it might be + the result of a -d argument in the modules file). It might be + cleaner if we genuinely expanded module names, all the way to a + local directory and repository, but that isn't the way it works + now. */ + send_file_names (module_argc, module_argv, 0); + + for (i = 0; i < modules_count; ++i) + { + argv[0] = where ? where : modules_vector[i]; + if (isfile (argv[0])) + send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0); + } + send_a_repository ("", current_parsed_root->directory, ""); +} + +void +client_nonexpanded_setup( void ) +{ + send_a_repository ("", current_parsed_root->directory, ""); +} + +/* Receive a cvswrappers line from the server; it must be a line + containing an RCS option (e.g., "*.exe -k 'b'"). + + Note that this doesn't try to handle -t/-f options (which are a + whole separate issue which noone has thought much about, as far + as I know). + + We need to know the keyword expansion mode so we know whether to + read the file in text or binary mode. */ + +static void +handle_wrapper_rcs_option( char *args, int len ) +{ + char *p; + + /* Enforce the notes in cvsclient.texi about how the response is not + as free-form as it looks. */ + p = strchr (args, ' '); + if (p == NULL) + goto handle_error; + if (*++p != '-' + || *++p != 'k' + || *++p != ' ' + || *++p != '\'') + goto handle_error; + if (strchr (p, '\'') == NULL) + goto handle_error; + + /* Add server-side cvswrappers line to our wrapper list. */ + wrap_add (args, 0); + return; + handle_error: + error (0, errno, "protocol error: ignoring invalid wrappers %s", args); +} + + +static void +handle_m( char *args, int len ) +{ + /* 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 (is that true? I think the comment is probably + based on being confused between default buffering between + stdout and stderr. But I'm not sure). */ + fflush (stderr); + fwrite (args, len, sizeof (*args), stdout); + putc ('\n', stdout); +} + +static void handle_mbinary (char *, int); + +static void +handle_mbinary( char *args, int len ) +{ + char *size_string; + size_t size; + size_t totalread; + size_t nread; + size_t toread; + char buf[8192]; + + /* See comment at handle_m about (non)flush of stderr. */ + + /* Get the size. */ + read_line (&size_string); + size = atoi (size_string); + free (size_string); + + /* OK, now get all the data. The algorithm here is that we read + as much as the network wants to give us in + try_read_from_server, and then we output it all, and then + repeat, until we get all the data. */ + totalread = 0; + while (totalread < size) + { + toread = size - totalread; + if (toread > sizeof buf) + toread = sizeof buf; + + nread = try_read_from_server (buf, toread); + cvs_output_binary (buf, nread); + totalread += nread; + } +} + +static void +handle_e( char *args, int len ) +{ + /* 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( char *args, int len ) +{ + fflush (stderr); +} + +static void handle_mt (char *, int); + +static void +handle_mt( char *args, int len ) +{ + char *p; + char *tag = args; + char *text; + + /* See comment at handle_m for more details. */ + fflush (stderr); + + p = strchr (args, ' '); + if (p == NULL) + text = NULL; + else + { + *p++ = '\0'; + text = p; + } + + switch (tag[0]) + { + case '+': + if (strcmp (tag, "+updated") == 0) + updated_seen = 1; + else if (strcmp (tag, "+importmergecmd") == 0) + importmergecmd.seen = 1; + break; + case '-': + if (strcmp (tag, "-updated") == 0) + updated_seen = 0; + else if (strcmp (tag, "-importmergecmd") == 0) + { + char buf[80]; + + /* Now that we have gathered the information, we can + output the suggested merge command. */ + + if (importmergecmd.conflicts == 0 + || importmergecmd.mergetag1 == NULL + || importmergecmd.mergetag2 == NULL + || importmergecmd.repository == NULL) + { + error (0, 0, + "invalid server: incomplete importmergecmd tags"); + break; + } + + sprintf (buf, "\n%d conflicts created by this import.\n", + importmergecmd.conflicts); + cvs_output (buf, 0); + cvs_output ("Use the following command to help the merge:\n\n", + 0); + cvs_output ("\t", 1); + cvs_output (program_name, 0); + if (CVSroot_cmdline != NULL) + { + cvs_output (" -d ", 0); + cvs_output (CVSroot_cmdline, 0); + } + cvs_output (" checkout -j", 0); + cvs_output (importmergecmd.mergetag1, 0); + cvs_output (" -j", 0); + cvs_output (importmergecmd.mergetag2, 0); + cvs_output (" ", 1); + cvs_output (importmergecmd.repository, 0); + cvs_output ("\n\n", 0); + + /* Clear the static variables so that everything is + ready for any subsequent importmergecmd tag. */ + importmergecmd.conflicts = 0; + free (importmergecmd.mergetag1); + importmergecmd.mergetag1 = NULL; + free (importmergecmd.mergetag2); + importmergecmd.mergetag2 = NULL; + free (importmergecmd.repository); + importmergecmd.repository = NULL; + + importmergecmd.seen = 0; + } + break; + default: + if (updated_seen) + { + if (strcmp (tag, "fname") == 0) + { + if (updated_fname != NULL) + { + /* Output the previous message now. This can happen + if there was no Update-existing or other such + response, due to the -n global option. */ + cvs_output ("U ", 0); + cvs_output (updated_fname, 0); + cvs_output ("\n", 1); + free (updated_fname); + } + updated_fname = xstrdup (text); + } + /* Swallow all other tags. Either they are extraneous + or they reflect future extensions that we can + safely ignore. */ + } + else if (importmergecmd.seen) + { + if (strcmp (tag, "conflicts") == 0) + importmergecmd.conflicts = atoi (text); + else if (strcmp (tag, "mergetag1") == 0) + importmergecmd.mergetag1 = xstrdup (text); + else if (strcmp (tag, "mergetag2") == 0) + importmergecmd.mergetag2 = xstrdup (text); + else if (strcmp (tag, "repository") == 0) + importmergecmd.repository = xstrdup (text); + /* Swallow all other tags. Either they are text for + which we are going to print our own version when we + see -importmergecmd, or they are future extensions + we can safely ignore. */ + } + else if (strcmp (tag, "newline") == 0) + printf ("\n"); + else if (strcmp (tag, "date") == 0) + { + char *date = format_date_alloc (text); + printf ("%s", date); + free (date); + } + else if (text != NULL) + printf ("%s", text); + } +} + +#endif /* CLIENT_SUPPORT */ +#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT) + +/* This table must be writeable if the server code is included. */ +struct response responses[] = +{ +#ifdef CLIENT_SUPPORT +#define RSP_LINE(n, f, t, s) {n, f, t, s} +#else /* ! CLIENT_SUPPORT */ +#define RSP_LINE(n, f, t, s) {n, s} +#endif /* CLIENT_SUPPORT */ + + RSP_LINE("ok", handle_ok, response_type_ok, rs_essential), + RSP_LINE("error", handle_error, response_type_error, rs_essential), + RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal, + rs_essential), + RSP_LINE("Checked-in", handle_checked_in, response_type_normal, + rs_essential), + RSP_LINE("New-entry", handle_new_entry, response_type_normal, rs_optional), + 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("Mod-time", handle_mod_time, 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, + rs_optional), + RSP_LINE("Set-static-directory", handle_set_static_directory, + response_type_normal, + rs_optional), + RSP_LINE("Clear-static-directory", handle_clear_static_directory, + response_type_normal, + rs_optional), + RSP_LINE("Set-sticky", handle_set_sticky, response_type_normal, + rs_optional), + RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal, + rs_optional), + RSP_LINE("Template", handle_template, response_type_normal, + rs_optional), + RSP_LINE("Clear-template", handle_clear_template, response_type_normal, + rs_optional), + RSP_LINE("Notified", handle_notified, response_type_normal, rs_optional), + RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal, + rs_optional), + RSP_LINE("Wrapper-rcsOption", handle_wrapper_rcs_option, + response_type_normal, + rs_optional), + RSP_LINE("M", handle_m, response_type_normal, rs_essential), + RSP_LINE("Mbinary", handle_mbinary, response_type_normal, rs_optional), + RSP_LINE("E", handle_e, response_type_normal, rs_essential), + RSP_LINE("F", handle_f, response_type_normal, rs_optional), + RSP_LINE("MT", handle_mt, response_type_normal, rs_optional), + /* Possibly should be response_type_error. */ + RSP_LINE(NULL, NULL, response_type_normal, rs_essential) + +#undef RSP_LINE +}; + +#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */ +#ifdef CLIENT_SUPPORT + + + +/* + * If LEN is 0, then send_to_server_via() computes string's length itself. + * + * Therefore, pass the real length when transmitting data that might + * contain 0's. + */ +void +send_to_server_via (struct buffer *via_buffer, const char *str, size_t len) +{ + static int nbytes; + + if (len == 0) + len = strlen (str); + + buf_output (via_buffer, str, len); + + /* 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 (via_buffer); + if (status != 0) + error (1, status, "error writing to server"); + nbytes = 0; + } +} + + + +void +send_to_server (const char *str, size_t len) +{ + send_to_server_via (global_to_server, str, len); +} + + + +/* 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( char *buf, size_t len ) +{ + int status, nread; + char *data; + + status = buf_read_data (global_from_server, len, &data, &nread); + if (status != 0) + { + 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"); + } + + memcpy (buf, data, nread); + + return nread; +} + +/* + * Read LEN bytes from the server or die trying. + */ +void +read_from_server( char *buf, size_t len ) +{ + size_t red = 0; + while (red < len) + { + red += try_read_from_server (buf + red, len - red); + if (red == len) + break; + } +} + +/* + * Get some server responses and process them. Returns nonzero for + * error, 0 for success. */ +int +get_server_responses( void ) +{ + struct response *rs; + do + { + char *cmd; + int len; + + len = read_line (&cmd); + for (rs = responses; rs->name != NULL; ++rs) + if (strncmp (cmd, rs->name, strlen (rs->name)) == 0) + { + int cmdlen = strlen (rs->name); + if (cmd[cmdlen] == '\0') + ; + else if (cmd[cmdlen] == ' ') + ++cmdlen; + else + /* + * The first len characters match, but it's a different + * response. e.g. the response is "oklahoma" but we + * matched "ok". + */ + continue; + (*rs->func) (cmd + cmdlen, len - cmdlen); + break; + } + if (rs->name == NULL) + /* It's OK to print just to the first '\0'. */ + /* We might want to handle control characters and the like + in some other way other than just sending them to stdout. + One common reason for this error is if people use :ext: + with a version of rsh which is doing CRLF translation or + something, and so the client gets "ok^M" instead of "ok". + Right now that will tend to print part of this error + message over the other part of it. It seems like we could + do better (either in general, by quoting or omitting all + control characters, and/or specifically, by detecting the CRLF + case and printing a specific error message). */ + error (0, 0, + "warning: unrecognized response `%s' from cvs server", + cmd); + free (cmd); + } while (rs->type == response_type_normal); + + if (updated_fname != NULL) + { + /* Output the previous message now. This can happen + if there was no Update-existing or other such + response, due to the -n global option. */ + cvs_output ("U ", 0); + cvs_output (updated_fname, 0); + cvs_output ("\n", 1); + free (updated_fname); + updated_fname = NULL; + } + + if (rs->type == response_type_error) + return 1; + if (failure_exit) + return 1; + return 0; +} + + + +/* Get the responses and then close the connection. */ + +/* + * Flag var; we'll set it in start_server() and not one of its + * callees, such as start_rsh_server(). This means that there might + * be a small window between the starting of the server and the + * setting of this var, but all the code in that window shouldn't care + * because it's busy checking return values to see if the server got + * started successfully anyway. + */ +int server_started = 0; + +int +get_responses_and_close( void ) +{ + int errs = get_server_responses (); + int status; + + /* The following is necessary when working with multiple cvsroots, at least + * with commit. It used to be buried nicely in do_deferred_progs() before + * that function was removed. I suspect it wouldn't be necessary if + * call_in_directory() saved its working directory via save_cwd() before + * changing its directory and restored the saved working directory via + * restore_cwd() before exiting. Of course, calling CVS_CHDIR only once, + * here, may be more efficient. + */ + if( toplevel_wd != NULL ) + { + if( CVS_CHDIR( toplevel_wd ) < 0 ) + error( 1, errno, "could not chdir to %s", toplevel_wd ); + } + + if (client_prune_dirs) + process_prune_candidates (); + + /* First we shut down GLOBAL_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 GLOBAL_FROM_SERVER will complete. + */ + + status = buf_shutdown (global_to_server); + if (status != 0) + error (0, status, "shutting down buffer to server"); + buf_free (global_to_server); + global_to_server = NULL; + + status = buf_shutdown (global_from_server); + if (status != 0) + error (0, status, "shutting down buffer from server"); + buf_free (global_from_server); + global_from_server = NULL; + server_started = 0; + + /* see if we need to sleep before returning to avoid time-stamp races */ + if (last_register_time) + { + sleep_past (last_register_time); + } + + return errs; +} + +int +supported_request( char *name ) +{ + struct request *rq; + + for (rq = requests; rq->name; rq++) + if (!strcmp (rq->name, name)) + return (rq->flags & RQ_SUPPORTED) != 0; + error (1, 0, "internal error: testing support for unknown option?"); + /* NOTREACHED */ + return 0; +} + + + +#if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI) + + +/* Generic function to do port number lookup tasks. + * + * In order of precedence, will return: + * getenv (envname), if defined + * getservbyname (portname), if defined + * defaultport + */ +static int +get_port_number (const char *envname, const char *portname, int defaultport) +{ + struct servent *s; + char *port_s; + + if (envname && (port_s = getenv (envname))) + { + int port = atoi (port_s); + if (port <= 0) + { + error (0, 0, "%s must be a positive integer! If you", envname); + 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."); + } + return port; + } + else if (portname && (s = getservbyname (portname, "tcp"))) + return ntohs (s->s_port); + else + return defaultport; +} + + + +/* get the port number for a client to connect to based on the port + * and method of a cvsroot_t. + * + * we do this here instead of in parse_cvsroot so that we can keep network + * code confined to a localized area and also to delay the lookup until the + * last possible moment so it remains possible to run cvs client commands that + * skip opening connections to the server (i.e. skip network operations + * entirely) + * + * and yes, I know none of the commands do that now, but here's to planning + * for the future, eh? cheers. + */ +int +get_cvs_port_number (const cvsroot_t *root) +{ + + if (root->port) return root->port; + + switch (root->method) + { +# ifdef HAVE_GSSAPI + case gserver_method: +# endif /* HAVE_GSSAPI */ +# ifdef AUTH_CLIENT_SUPPORT + case pserver_method: +# endif /* AUTH_CLIENT_SUPPORT */ +# if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) + return get_port_number ("CVS_CLIENT_PORT", "cvspserver", + CVS_AUTH_PORT); +# endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) */ +# ifdef HAVE_KERBEROS + case kserver_method: + return get_port_number ("CVS_CLIENT_PORT", "cvs", CVS_PORT); +# endif /* HAVE_KERBEROS */ + default: + error(1, EINVAL, +"internal error: get_cvs_port_number called for invalid connection method (%s)", + method_names[root->method]); + break; + } + /* NOTREACHED */ + return -1; +} + + + +/* get the port number for a client to connect to based on the proxy port + * of a cvsroot_t. + */ +static int +get_proxy_port_number (const cvsroot_t *root) +{ + + if (root->proxy_port) return root->proxy_port; + + return get_port_number ("CVS_PROXY_PORT", NULL, CVS_PROXY_PORT); +} + + + +void +make_bufs_from_fds( int tofd, int fromfd, int child_pid, + struct buffer **to_server_p, + struct buffer **from_server_p, int is_sock ) +{ + FILE *to_server_fp; + FILE *from_server_fp; + +# ifdef NO_SOCKET_TO_FD + if (is_sock) + { + assert (tofd == fromfd); + *to_server_p = socket_buffer_initialize (tofd, 0, + (BUFMEMERRPROC) NULL); + *from_server_p = socket_buffer_initialize (tofd, 1, + (BUFMEMERRPROC) NULL); + } + else +# endif /* NO_SOCKET_TO_FD */ + { + /* todo: some OS's don't need these calls... */ + close_on_exec (tofd); + close_on_exec (fromfd); + + /* SCO 3 and AIX have a nasty bug in the I/O libraries which precludes + fdopening the same file descriptor twice, so dup it if it is the + same. */ + if (tofd == fromfd) + { + fromfd = dup (tofd); + if (fromfd < 0) + error (1, errno, "cannot dup net connection"); + } + + /* These will use binary mode on systems which have it. */ + /* + * Also, we know that from_server is shut down second, so we pass + * child_pid in there. In theory, it should be stored in both + * buffers with a ref count... + */ + to_server_fp = fdopen (tofd, FOPEN_BINARY_WRITE); + if (to_server_fp == NULL) + error (1, errno, "cannot fdopen %d for write", tofd); + *to_server_p = stdio_buffer_initialize (to_server_fp, 0, 0, + (BUFMEMERRPROC) NULL); + + from_server_fp = fdopen (fromfd, FOPEN_BINARY_READ); + if (from_server_fp == NULL) + error (1, errno, "cannot fdopen %d for read", fromfd); + *from_server_p = stdio_buffer_initialize (from_server_fp, child_pid, 1, + (BUFMEMERRPROC) NULL); + } +} +#endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) || defined(HAVE_GSSAPI) */ + + + +#if defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI) +/* Connect to the authenticating server. + + If VERIFY_ONLY is non-zero, then just verify that the password is + correct and then shutdown the connection. + + If VERIFY_ONLY is 0, then really connect to the server. + + If DO_GSSAPI is non-zero, then we use GSSAPI authentication rather + than the pserver password authentication. + + If we fail to connect or if access is denied, then die with fatal + error. */ +void +connect_to_pserver (cvsroot_t *root, struct buffer **to_server_p, + struct buffer **from_server_p, int verify_only, + int do_gssapi) +{ + int sock; + int port_number, proxy_port_number; + struct sockaddr_in client_sai; + struct hostent *hostinfo; + struct buffer *to_server, *from_server; + + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock == -1) + error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO)); + port_number = get_cvs_port_number (root); + + /* if we have a proxy connect to that instead */ + if (root->proxy_hostname) + { + proxy_port_number = get_proxy_port_number (root); + hostinfo = init_sockaddr (&client_sai, root->proxy_hostname, + proxy_port_number); + TRACE (1, "Connecting to %s:%d via proxy %s(%s):%d.", + root->hostname, port_number, root->proxy_hostname, + inet_ntoa (client_sai.sin_addr), proxy_port_number); + } + else + { + hostinfo = init_sockaddr (&client_sai, root->hostname, port_number); + TRACE (1, "Connecting to %s(%s):%d.", + root->hostname, + inet_ntoa (client_sai.sin_addr), port_number); + } + + if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai)) + < 0) + error (1, 0, "connect to %s(%s):%d failed: %s", + root->proxy_hostname ? root->proxy_hostname : root->hostname, + inet_ntoa (client_sai.sin_addr), + root->proxy_hostname ? proxy_port_number : port_number, + SOCK_STRERROR (SOCK_ERRNO)); + + make_bufs_from_fds (sock, sock, 0, &to_server, &from_server, 1); + + /* if we have proxy then connect to the proxy first */ + if (root->proxy_hostname) + { +#define CONNECT_STRING "CONNECT %s:%d HTTP/1.0\r\n\r\n" + /* Send a "CONNECT" command to proxy: */ + char* read_buf; + int codenum; + size_t count; + /* 4 characters for port covered by the length of %s & %d */ + char* write_buf = asnprintf (NULL, &count, CONNECT_STRING, + root->hostname, port_number); + send_to_server_via (to_server, write_buf, count); + + /* Wait for HTTP status code, bail out if you don't get back a 2xx + * code. + */ + read_line_via (from_server, to_server, &read_buf); + sscanf (read_buf, "%s %d", write_buf, &codenum); + + if ((codenum / 100) != 2) + error (1, 0, "proxy server %s:%d does not support http tunnelling", + root->proxy_hostname, proxy_port_number); + free (read_buf); + free (write_buf); + + /* Skip through remaining part of MIME header, recv_line + consumes the trailing \n */ + while (read_line_via (from_server, to_server, &read_buf) > 0) + { + if (read_buf[0] == '\r' || read_buf[0] == 0) + { + free (read_buf); + break; + } + free (read_buf); + } + } + + auth_server (root, to_server, from_server, verify_only, do_gssapi, + hostinfo); + + if (verify_only) + { + int status; + + status = buf_shutdown (to_server); + if (status != 0) + error (0, status, "shutting down buffer to server"); + buf_free (to_server); + to_server = NULL; + + status = buf_shutdown (from_server); + if (status != 0) + error (0, status, "shutting down buffer from server"); + buf_free (from_server); + from_server = NULL; + + /* Don't need to set server_started = 0 since we don't set it to 1 + * until returning from this call. + */ + } + else + { + *to_server_p = to_server; + *from_server_p = from_server; + } + + return; +} + + + +static void +auth_server (cvsroot_t *root, struct buffer *to_server, + struct buffer *from_server, int verify_only, int do_gssapi, + struct hostent *hostinfo) +{ + char *username = NULL; /* the username we use to connect */ + char no_passwd = 0; /* gets set if no password found */ + + /* Run the authorization mini-protocol before anything else. */ + if (do_gssapi) + { +# ifdef HAVE_GSSAPI + FILE *fp = stdio_buffer_get_file (to_server); + int fd = fp ? fileno (fp) : -1; + struct stat s; + + if ((fd < 0) || (fstat (fd, &s) < 0) || !S_ISSOCK(s.st_mode)) + { + error (1, 0, + "gserver currently only enabled for socket connections"); + } + + if (! connect_to_gserver (root, fd, hostinfo)) + { + error (1, 0, + "authorization failed: server %s rejected access to %s", + root->hostname, root->directory); + } +# else /* ! HAVE_GSSAPI */ + error (1, 0, +"INTERNAL ERROR: This client does not support GSSAPI authentication"); +# endif /* HAVE_GSSAPI */ + } + else /* ! do_gssapi */ + { +# ifdef AUTH_CLIENT_SUPPORT + char *begin = NULL; + char *password = NULL; + char *end = NULL; + + if (verify_only) + { + begin = "BEGIN VERIFICATION REQUEST"; + end = "END VERIFICATION REQUEST"; + } + else + { + begin = "BEGIN AUTH REQUEST"; + end = "END AUTH REQUEST"; + } + + /* Get the password, probably from ~/.cvspass. */ + password = get_cvs_password (); + username = root->username ? root->username : getcaller(); + + /* Send the empty string by default. This is so anonymous CVS + access doesn't require client to have done "cvs login". */ + if (password == NULL) + { + no_passwd = 1; + password = scramble (""); + } + + /* Announce that we're starting the authorization protocol. */ + send_to_server_via(to_server, begin, 0); + send_to_server_via(to_server, "\012", 1); + + /* Send the data the server needs. */ + send_to_server_via(to_server, root->directory, 0); + send_to_server_via(to_server, "\012", 1); + send_to_server_via(to_server, username, 0); + send_to_server_via(to_server, "\012", 1); + send_to_server_via(to_server, password, 0); + send_to_server_via(to_server, "\012", 1); + + /* Announce that we're ending the authorization protocol. */ + send_to_server_via(to_server, end, 0); + send_to_server_via(to_server, "\012", 1); + + /* Paranoia. */ + memset (password, 0, strlen (password)); +# else /* ! AUTH_CLIENT_SUPPORT */ + error (1, 0, "INTERNAL ERROR: This client does not support pserver authentication"); +# endif /* AUTH_CLIENT_SUPPORT */ + } /* if (do_gssapi) */ + + { + char *read_buf; + + /* Loop, getting responses from the server. */ + while (1) + { + read_line_via (from_server, to_server, &read_buf); + + if (strcmp (read_buf, "I HATE YOU") == 0) + { + /* Authorization not granted. + * + * This is a little confusing since we can reach this while + * loop in GSSAPI mode, but if GSSAPI authentication failed, + * we already jumped to the rejected label (there is no case + * where the connect_to_gserver function can return 1 and we + * will not receive "I LOVE YOU" from the server, barring + * broken connections and garbled messages, of course). The + * GSSAPI case is also the case where username can be NULL + * since username is initialized in the !gssapi section. + * + * i.e. This is a pserver specific error message and should be + * since GSSAPI doesn't use username. + */ + error (0, 0, +"authorization failed: server %s rejected access to %s for user %s", + root->hostname, root->directory, + username ? username : "(null)"); + + /* Output a special error message if authentication was attempted + with no password -- the user should be made aware that they may + have missed a step. */ + if (no_passwd) + { + error (0, 0, +"used empty password; try \"cvs login\" with a real password"); + } + exit (EXIT_FAILURE); + } + else if (strncmp (read_buf, "E ", 2) == 0) + { + fprintf (stderr, "%s\n", read_buf + 2); + + /* Continue with the authentication protocol. */ + } + else if (strncmp (read_buf, "error ", 6) == 0) + { + char *p; + + /* First skip the code. */ + p = read_buf + 6; + while (*p != ' ' && *p != '\0') + ++p; + + /* Skip the space that follows the code. */ + if (*p == ' ') + ++p; + + /* Now output the text. */ + fprintf (stderr, "%s\n", p); + exit (EXIT_FAILURE); + } + else if (strcmp (read_buf, "I LOVE YOU") == 0) + { + free (read_buf); + break; + } + else + { + error (1, 0, + "unrecognized auth response from %s: %s", + root->hostname, read_buf); + } + free (read_buf); + } + } +} +#endif /* defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI) */ + + + +#ifdef CLIENT_SUPPORT +/* + * Connect to a forked server process. + */ +static void +connect_to_forked_server (struct buffer **to_server_p, + struct buffer **from_server_p) +{ + int tofd, fromfd; + int child_pid; + + /* This is pretty simple. All we need to do is choose the correct + cvs binary and call piped_child. */ + + char *command[3]; + + command[0] = getenv ("CVS_SERVER"); + if (!command[0]) +# ifdef SERVER_SUPPORT + /* FIXME: + * I'm casting out the const below because I know that piped_child, the + * only function we pass COMMAND to, accepts COMMAND as a + * (char *const *) and won't alter it, and we don't alter it in this + * function. This is yucky, there should be a way to declare COMMAND + * such that this casting isn't needed, but I don't know how. If I + * declare it as (const char *command[]), the compiler complains about + * an incompatible arg 1 being passed to piped_child and if I declare + * it as (char *const command[3]), then the compiler complains when I + * assign values to command[i]. + */ + command[0] = (char *)program_path; +# else /* SERVER_SUPPORT */ + { + error( 0, 0, "You must set the CVS_SERVER environment variable when" ); + error( 0, 0, "using the :fork: access method." ); + error( 1, 0, "This CVS was not compiled with server support." ); + } +# endif /* SERVER_SUPPORT */ + + command[1] = "server"; + command[2] = NULL; + + TRACE (TRACE_FUNCTION, "Forking server: %s %s\n", + command[0] ? command[0] : "(null)", command[1]); + + child_pid = piped_child (command, &tofd, &fromfd); + if (child_pid < 0) + error (1, 0, "could not fork server process"); + + make_bufs_from_fds (tofd, fromfd, child_pid, to_server_p, from_server_p, 0); +} +#endif /* CLIENT_SUPPORT */ + + + +static int +send_variable_proc (Node *node, void *closure) +{ + send_to_server ("Set ", 0); + send_to_server (node->key, 0); + send_to_server ("=", 1); + send_to_server (node->data, 0); + send_to_server ("\012", 1); + return 0; +} + + + +/* Contact the server. */ +void +start_server (void) +{ + int rootless; + + /* Clear our static variables for this invocation. */ + if (toplevel_repos != NULL) + free (toplevel_repos); + toplevel_repos = NULL; + + /* 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. */ + + switch (current_parsed_root->method) + { + +#ifdef AUTH_CLIENT_SUPPORT + case pserver_method: + /* Toss the return value. It will die with an error message if + * anything goes wrong anyway. + */ + connect_to_pserver (current_parsed_root, &global_to_server, + &global_from_server, 0, 0); + break; +#endif /* AUTH_CLIENT_SUPPORT */ + +#if HAVE_KERBEROS + case kserver_method: + start_kerberos4_server (current_parsed_root, &global_to_server, + &global_from_server); + break; +#endif /* HAVE_KERBEROS */ + +#ifdef HAVE_GSSAPI + case gserver_method: + /* GSSAPI authentication is handled by the pserver. */ + connect_to_pserver (current_parsed_root, &global_to_server, + &global_from_server, 0, 1); + break; +#endif /* HAVE_GSSAPI */ + + case ext_method: +#ifdef NO_EXT_METHOD + error (0, 0, ":ext: method not supported by this port of CVS"); + error (1, 0, "try :server: instead"); +#else /* ! NO_EXT_METHOD */ + start_rsh_server (current_parsed_root, &global_to_server, + &global_from_server); +#endif /* NO_EXT_METHOD */ + break; + + case server_method: +#ifdef START_SERVER + { + int tofd, fromfd; + START_SERVER (&tofd, &fromfd, getcaller (), + current_parsed_root->username, + current_parsed_root->hostname, + current_parsed_root->directory); +# ifdef START_SERVER_RETURNS_SOCKET + make_bufs_from_fds (tofd, fromfd, 0, &global_to_server, + &global_from_server, 1); +# else /* ! START_SERVER_RETURNS_SOCKET */ + make_bufs_from_fds (tofd, fromfd, 0, &global_to_server, + &global_from_server, 0); +# endif /* START_SERVER_RETURNS_SOCKET */ + } +#else /* ! START_SERVER */ + /* 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 /* START_SERVER */ + break; + + case fork_method: + connect_to_forked_server (&global_to_server, &global_from_server); + break; + + 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; + + setup_logfiles(&global_to_server, &global_from_server); + + /* Clear static variables. */ + if (toplevel_repos != NULL) + free (toplevel_repos); + toplevel_repos = NULL; + if (last_repos != NULL) + free (last_repos); + last_repos = NULL; + if (last_update_dir != NULL) + free (last_update_dir); + last_update_dir = NULL; + stored_checksum_valid = 0; + if (stored_mode != NULL) + { + free (stored_mode); + stored_mode = NULL; + } + + rootless = (strcmp (cvs_cmd_name, "init") == 0); + if (!rootless) + { + send_to_server ("Root ", 0); + send_to_server (current_parsed_root->directory, 0); + send_to_server ("\012", 1); + } + + { + struct response *rs; + + send_to_server ("Valid-responses", 0); + + for (rs = responses; rs->name != NULL; ++rs) + { + send_to_server (" ", 0); + send_to_server (rs->name, 0); + } + send_to_server ("\012", 1); + } + send_to_server ("valid-requests\012", 0); + + if (get_server_responses ()) + exit (EXIT_FAILURE); + + /* + * Now handle global options. + * + * -H, -f, -d, -e should be handled OK locally. + * + * -b we ignore (treating it as a server installation issue). + * FIXME: should be an error message. + * + * -v we print local version info; FIXME: Add a protocol request to get + * the version from the server so we can print that too. + * + * -l -t -r -w -q -n and -Q need to go to the server. + */ + + { + int have_global = supported_request ("Global_option"); + + if (noexec) + { + if (have_global) + { + send_to_server ("Global_option -n\012", 0); + } + else + error (1, 0, + "This server does not support the global -n option."); + } + if (quiet) + { + if (have_global) + { + send_to_server ("Global_option -q\012", 0); + } + else + error (1, 0, + "This server does not support the global -q option."); + } + if (really_quiet) + { + if (have_global) + { + send_to_server ("Global_option -Q\012", 0); + } + else + error (1, 0, + "This server does not support the global -Q option."); + } + if (!cvswrite) + { + if (have_global) + { + send_to_server ("Global_option -r\012", 0); + } + else + error (1, 0, + "This server does not support the global -r option."); + } + if (trace) + { + if (have_global) + { + int count = trace; + while (count--) send_to_server ("Global_option -t\012", 0); + } + else + error (1, 0, + "This server does not support the global -t option."); + } + } + + /* Find out about server-side cvswrappers. An extra network + turnaround for cvs import seems to be unavoidable, unless we + want to add some kind of client-side place to configure which + filenames imply binary. For cvs add, we could avoid the + problem by keeping a copy of the wrappers in CVSADM (the main + reason to bother would be so we could make add work without + contacting the server, I suspect). */ + + if ((strcmp (cvs_cmd_name, "import") == 0) + || (strcmp (cvs_cmd_name, "add") == 0)) + { + if (supported_request ("wrapper-sendme-rcsOptions")) + { + int err; + send_to_server ("wrapper-sendme-rcsOptions\012", 0); + err = get_server_responses (); + if (err != 0) + error (err, 0, "error reading from server"); + } + } + + if (cvsencrypt && !rootless) + { +#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 (current_parsed_root->method == kserver_method) + { + if (! supported_request ("Kerberos-encrypt")) + error (1, 0, "This server does not support encryption"); + send_to_server ("Kerberos-encrypt\012", 0); + initialize_kerberos4_encryption_buffers (&global_to_server, + &global_from_server); + } + else +#endif /* HAVE_KERBEROS */ +#ifdef HAVE_GSSAPI + if (current_parsed_root->method == gserver_method) + { + if (! supported_request ("Gssapi-encrypt")) + error (1, 0, "This server does not support encryption"); + send_to_server ("Gssapi-encrypt\012", 0); + initialize_gssapi_buffers(&global_to_server, &global_from_server); + cvs_gssapi_encrypt = 1; + } + else +#endif /* HAVE_GSSAPI */ + error (1, 0, +"Encryption is only supported when using GSSAPI or Kerberos"); +#else /* ! ENCRYPTION */ + error (1, 0, "This client does not support encryption"); +#endif /* ! ENCRYPTION */ + } + + if (gzip_level && !rootless) + { + 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. */ + + global_to_server = compress_buffer_initialize (global_to_server, 0, + gzip_level, NULL); + global_from_server = compress_buffer_initialize (global_from_server, + 1, gzip_level, + NULL); + } +#ifndef NO_CLIENT_GZIP_PROCESS + else if (supported_request ("gzip-file-contents")) + { + char gzip_level_buf[5]; + send_to_server ("gzip-file-contents ", 0); + sprintf (gzip_level_buf, "%d", gzip_level); + 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; + } + } + + if (cvsauthenticate && ! cvsencrypt && !rootless) + { + /* Turn on authentication after turning on compression, so + that we can compress the authentication information. We + assume that encrypted data is always authenticated--the + ability to decrypt the data stream is itself a form of + authentication. */ +#ifdef HAVE_GSSAPI + if (current_parsed_root->method == gserver_method) + { + if (! supported_request ("Gssapi-authenticate")) + error (1, 0, + "This server does not support stream authentication"); + send_to_server ("Gssapi-authenticate\012", 0); + initialize_gssapi_buffers(&global_to_server, &global_from_server); + + } + else + error (1, 0, "Stream authentication is only supported when using GSSAPI"); +#else /* ! HAVE_GSSAPI */ + error (1, 0, "This client does not support stream authentication"); +#endif /* ! HAVE_GSSAPI */ + } + + /* If "Set" is not supported, just silently fail to send the variables. + Users with an old server should get a useful error message when it + fails to recognize the ${=foo} syntax. This way if someone uses + several servers, some of which are new and some old, they can still + set user variables in their .cvsrc without trouble. */ + if (supported_request ("Set")) + walklist (variable_list, send_variable_proc, NULL); +} + + + +/* Send an argument STRING. */ +void +send_arg (char *string) +{ + char buf[1]; + char *p = string; + + send_to_server ("Argument ", 0); + + while (*p) + { + if (*p == '\n') + { + send_to_server ("\012Argumentx ", 0); + } + else + { + buf[0] = *p; + send_to_server (buf, 1); + } + ++p; + } + send_to_server ("\012", 1); +} + + + +/* 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 (const char *file, const char *short_pathname, Vers_TS *vers) +{ + /* File was modified, send it. */ + struct stat sb; + int fd; + char *buf; + char *mode_string; + size_t bufsize; + int bin; + + TRACE (1, "Sending file `%s' to server", file); + + /* Don't think we can assume fstat exists. */ + if (CVS_STAT (file, &sb) < 0) + error (1, errno, "reading %s", short_pathname); + + mode_string = mode_to_string (sb.st_mode); + + /* Beware: on systems using CRLF line termination conventions, + the read and write functions will convert CRLF to LF, so the + number of characters read is not the same as sb.st_size. Text + files should always be transmitted using the LF convention, so + we don't want to disable this conversion. */ + bufsize = sb.st_size; + buf = xmalloc (bufsize); + + /* Is the file marked as containing binary data by the "-kb" flag? + If so, make sure to open it in binary mode: */ + + if (vers && vers->options) + bin = !(strcmp (vers->options, "-kb")); + else + bin = 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 (file_gzip_level && sb.st_size > 100) + { + size_t newsize = 0; + + if (read_and_gzip (fd, short_pathname, (unsigned char **)&buf, + &bufsize, &newsize, + file_gzip_level)) + error (1, 0, "aborting due to compression error"); + + if (close (fd) < 0) + error (0, errno, "warning: can't close %s", short_pathname); + + { + char tmp[80]; + + send_to_server ("Modified ", 0); + send_to_server (file, 0); + send_to_server ("\012", 1); + send_to_server (mode_string, 0); + send_to_server ("\012z", 2); + sprintf (tmp, "%lu\n", (unsigned long) newsize); + send_to_server (tmp, 0); + + send_to_server (buf, newsize); + } + } + else + { + int newsize; + + { + char *bufp = buf; + int len; + + /* FIXME: This is gross. It assumes that we might read + less than st_size bytes (true on NT), but not more. + Instead of this we should just be reading a block of + data (e.g. 8192 bytes), writing it to the network, and + so on until EOF. */ + while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0) + bufp += len; + + if (len < 0) + error (1, errno, "reading %s", short_pathname); + + newsize = bufp - buf; + } + if (close (fd) < 0) + error (0, errno, "warning: can't close %s", short_pathname); + + { + char tmp[80]; + + send_to_server ("Modified ", 0); + send_to_server (file, 0); + send_to_server ("\012", 1); + send_to_server (mode_string, 0); + send_to_server ("\012", 1); + 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); + } + free (buf); + free (mode_string); +} + + + +/* 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; + int backup_modified; +}; + +/* Deal with one file. */ +static int +send_fileproc (void *callerdat, struct file_info *finfo) +{ + struct send_data *args = callerdat; + Vers_TS *vers; + struct file_info xfinfo; + /* File name to actually use. Might differ in case from + finfo->file. */ + const char *filename; + + send_a_repository ("", finfo->repository, finfo->update_dir); + + 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) + { + /* The Entries request. */ + send_to_server ("Entry /", 0); + send_to_server (filename, 0); + send_to_server ("/", 0); + send_to_server (vers->vn_user, 0); + send_to_server ("/", 0); + if (vers->ts_conflict != NULL) + { + if (vers->ts_user != NULL && + strcmp (vers->ts_conflict, vers->ts_user) == 0) + send_to_server ("+=", 0); + else + send_to_server ("+modified", 0); + } + send_to_server ("/", 0); + send_to_server (vers->entdata != NULL + ? vers->entdata->options + : vers->options, + 0); + send_to_server ("/", 0); + if (vers->entdata != NULL && vers->entdata->tag) + { + send_to_server ("T", 0); + send_to_server (vers->entdata->tag, 0); + } + else if (vers->entdata != NULL && vers->entdata->date) + { + send_to_server ("D", 0); + send_to_server (vers->entdata->date, 0); + } + send_to_server ("\012", 1); + } + else + { + /* It seems a little silly to re-read this on each file, but + send_dirent_proc doesn't get called if filenames are specified + explicitly on the command line. */ + wrap_add_file (CVSDOTWRAPPER, 1); + + if (wrap_name_has (filename, WRAP_RCSOPTION)) + { + /* No "Entry", but the wrappers did give us a kopt so we better + send it with "Kopt". As far as I know this only happens + for "cvs add". Question: is there any reason why checking + for options from wrappers isn't done in Version_TS? + + Note: it might have been better to just remember all the + kopts on the client side, rather than send them to the server, + and have it send us back the same kopts. But that seemed like + a bigger change than I had in mind making now. */ + + if (supported_request ("Kopt")) + { + char *opt; + + send_to_server ("Kopt ", 0); + opt = wrap_rcsoption (filename, 1); + send_to_server (opt, 0); + send_to_server ("\012", 1); + free (opt); + } + else + error (0, 0, "\ +warning: ignoring -k options due to server limitations"); + } + } + + if (vers->ts_user == NULL) + { + /* + * Do we want to print "file was lost" like normal CVS? + * Would it always be appropriate? + */ + /* 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) + { + 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); + + if (args->backup_modified) + { + char *bakname; + bakname = backup_file (filename, vers->vn_user); + /* This behavior is sufficiently unexpected to + justify overinformativeness, I think. */ + if (! really_quiet) + printf ("(Locally modified %s moved to %s)\n", + filename, bakname); + free (bakname); + } + } + else + { + 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 */ + if (ignlist) + { + Node *p; + + p = getnode (); + p->type = FILES; + p->key = xstrdup (finfo->file); + (void) addnode (ignlist, p); + } + + freevers_ts (&vers); + return 0; +} + + + +static void +send_ignproc (const char *file, const char *dir) +{ + if (ign_inhibit_server || !supported_request ("Questionable")) + { + if (dir[0] != '\0') + (void) printf ("? %s/%s\n", dir, file); + else + (void) printf ("? %s\n", file); + } + else + { + send_to_server ("Questionable ", 0); + send_to_server (file, 0); + send_to_server ("\012", 1); + } +} + + + +static int +send_filesdoneproc (void *callerdat, int err, const char *repository, + const char *update_dir, List *entries) +{ + /* if this directory has an ignore list, process it then free it */ + if (ignlist) + { + ignore_files (ignlist, entries, update_dir, send_ignproc); + dellist (&ignlist); + } + + return err; +} + + + +/* + * send_dirent_proc () is called back by the recursion processor before a + * sub-directory is processed for update. + * A return code of 0 indicates the directory should be + * processed by the recursion code. A return of non-zero indicates the + * recursion code should skip this directory. + * + */ +static Dtype +send_dirent_proc (void *callerdat, const char *dir, const char *repository, + const char *update_dir, List *entries) +{ + struct send_data *args = (struct send_data *) callerdat; + int dir_exists; + char *cvsadm_name; + + if (ignore_directory (update_dir)) + { + /* print the warm fuzzy message */ + if (!quiet) + error (0, 0, "Ignoring %s", 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); + + /* + * If there is an empty directory (e.g. we are doing `cvs add' on a + * newly-created directory), the server still needs to know about it. + */ + + if (dir_exists) + { + /* + * Get the repository from a CVS/Repository file whenever possible. + * The repository variable is wrong if the names in the local + * directory don't match the names in the repository. + */ + char *repos = Name_Repository (dir, update_dir); + send_a_repository (dir, repos, update_dir); + free (repos); + + /* initialize the ignore list for this directory */ + ignlist = getlist (); + } + else + { + /* It doesn't make sense to send a non-existent directory, + because there is no way to get the correct value for + the repository (I suppose maybe via the expand-modules + request). In the case where the "obvious" choice for + repository is correct, the server can figure out whether + to recreate the directory; in the case where it is wrong + (that is, does not match what modules give us), we might as + well just fail to recreate it. + + Checking for noexec is a kludge for "cvs -n add 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 (args->build_dirs && noexec) + send_a_repository (dir, repository, update_dir); + } + + return dir_exists ? R_PROCESS : R_SKIP_ALL; +} + + + +/* + * send_dirleave_proc () is called back by the recursion code upon leaving + * a directory. All it does is delete the ignore list if it hasn't already + * been done (by send_filesdone_proc). + */ +/* ARGSUSED */ +static int +send_dirleave_proc (void *callerdat, const char *dir, int err, + const char *update_dir, List *entries ) +{ + + /* Delete the ignore list if it hasn't already been done. */ + if (ignlist) + dellist (&ignlist); + return err; +} + +/* + * Send each option in a string to the server, one by one. + * This assumes that the options are separated by spaces, for example + * STRING might be "--foo -C5 -y". + */ + +void +send_option_string( char *string ) +{ + char *copy; + char *p; + + 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); +} + + + +/* Send the names of all the argument files to the server. */ +void +send_file_names (int argc, char **argv, unsigned int flags) +{ + int i; + + /* The fact that we do this here as well as start_recursion is a bit + of a performance hit. Perhaps worth cleaning up someday. */ + if (flags & SEND_EXPAND_WILD) + expand_wild (argc, argv, &argc, &argv); + + for (i = 0; i < argc; ++i) + { + char buf[1]; + char *p; +#ifdef FILENAMES_CASE_INSENSITIVE + char *line = NULL; +#endif /* FILENAMES_CASE_INSENSITIVE */ + + if (arg_should_not_be_sent_to_server (argv[i])) + continue; + +#ifdef FILENAMES_CASE_INSENSITIVE + /* We want to send the path as it appears in the + CVS/Entries files. We put this inside an ifdef + to avoid doing all these system calls in + cases where fncmp is just strcmp anyway. */ + /* The isdir (CVSADM) 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. */ + { + List *stack; + size_t line_len = 0; + char *q, *r; + struct saved_cwd sdir; + + /* Split the argument onto the stack. */ + stack = getlist(); + r = xstrdup (argv[i]); + /* It's okay to discard the const from the last_component return + * below since we know we passed in an arg that was not const. + */ + while ((q = (char *)last_component (r)) != r) + { + push (stack, xstrdup (q)); + *--q = '\0'; + } + push (stack, r); + + /* Normalize the path into outstr. */ + save_cwd (&sdir); + while (q = pop (stack)) + { + Node *node = NULL; + if (isdir (CVSADM)) + { + List *entries; + + /* 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, NULL); + node = findnode_fn (entries, q); + if (node != NULL) + { + /* Add the slash unless this is our first element. */ + if (line_len) + xrealloc_and_strcat (&line, &line_len, "/"); + xrealloc_and_strcat (&line, &line_len, node->key); + delnode (node); + } + Entries_Close (entries); + } + + /* If node is still NULL then we either didn't find CVSADM or + * we didn't find an entry there. + */ + if (node == NULL) + { + /* Add the slash unless this is our first element. */ + if (line_len) + xrealloc_and_strcat (&line, &line_len, "/"); + xrealloc_and_strcat (&line, &line_len, q); + break; + } + + /* And descend the tree. */ + if (isdir (q)) + CVS_CHDIR (q); + free (q); + } + restore_cwd (&sdir, NULL); + free_cwd (&sdir); + + /* Now put everything we didn't find entries for back on. */ + while (q = pop (stack)) + { + if (line_len) + xrealloc_and_strcat (&line, &line_len, "/"); + xrealloc_and_strcat (&line, &line_len, q); + free (q); + } + + p = line; + + dellist (&stack); + } +#else /* !FILENAMES_CASE_INSENSITIVE */ + p = argv[i]; +#endif /* FILENAMES_CASE_INSENSITIVE */ + + send_to_server ("Argument ", 0); + + while (*p) + { + if (*p == '\n') + { + send_to_server ("\012Argumentx ", 0); + } + else if (ISSLASH (*p)) + { + buf[0] = '/'; + send_to_server (buf, 1); + } + else + { + buf[0] = *p; + send_to_server (buf, 1); + } + ++p; + } + send_to_server ("\012", 1); +#ifdef FILENAMES_CASE_INSENSITIVE + free (line); +#endif /* FILENAMES_CASE_INSENSITIVE */ + } + + if (flags & SEND_EXPAND_WILD) + { + int i; + for (i = 0; i < argc; ++i) + free (argv[i]); + free (argv); + } +} + + + +/* Calculate and send max-dotdot to the server */ +static void +send_max_dotdot (argc, argv) + int argc; + char **argv; +{ + int i; + int level = 0; + int max_level = 0; + + /* Send Max-dotdot if needed. */ + for (i = 0; i < argc; ++i) + { + level = pathname_levels (argv[i]); + if (level > 0) + { + if (uppaths == NULL) uppaths = getlist(); + push_string (uppaths, xstrdup (argv[i])); + } + if (level > max_level) + max_level = level; + } + + if (max_level > 0) + { + if (supported_request ("Max-dotdot")) + { + char buf[10]; + sprintf (buf, "%d", max_level); + + send_to_server ("Max-dotdot ", 0); + send_to_server (buf, 0); + send_to_server ("\012", 1); + } + else + { + error (1, 0, +"backreference in path (`..') not supported by old (pre-Max-dotdot) servers"); + } + } +} + + + +/* 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 (int argc, char **argv, int local, int aflag, unsigned int flags) +{ + struct send_data args; + int err; + + send_max_dotdot (argc, argv); + + /* + * aflag controls whether the tag/date is copied into the vers_ts. + * 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; + args.backup_modified = flags & BACKUP_MODIFIED_FILES; + err = start_recursion + (send_fileproc, send_filesdoneproc, send_dirent_proc, + send_dirleave_proc, &args, argc, argv, local, W_LOCAL, aflag, + CVS_LOCK_NONE, NULL, 0, NULL); + if (err) + exit (EXIT_FAILURE); + if (toplevel_repos == NULL) + /* + * This happens if we are not processing any files, + * or for checkouts in directories without any existing stuff + * checked out. The following assignment is correct for the + * latter case; I don't think toplevel_repos matters for the + * former. + */ + toplevel_repos = xstrdup (current_parsed_root->directory); + send_repository ("", toplevel_repos, "."); +} + + + +void +client_import_setup (char *repository) +{ + if (toplevel_repos == NULL) /* should always be true */ + send_a_repository ("", repository, ""); +} + + + +/* + * Process the argument import file. + */ +int +client_process_import_file (char *message, char *vfile, char *vtag, int targc, + char *targv[], char *repository, + int all_files_binary, + int modtime /* Nonzero for "import -d". */ ) +{ + char *update_dir; + char *fullname; + Vers_TS vers; + + 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); + + 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 + { + vers.options = wrap_rcsoption (vfile, 1); + } + if (vers.options != NULL) + { + if (supported_request ("Kopt")) + { + send_to_server ("Kopt ", 0); + send_to_server (vers.options, 0); + send_to_server ("\012", 1); + } + else + error (0, 0, + "warning: ignoring -k options due to server limitations"); + } + if (modtime) + { + if (supported_request ("Checkin-time")) + { + struct stat sb; + char *rcsdate; + char netdate[MAXDATELEN]; + + if (CVS_STAT (vfile, &sb) < 0) + error (1, errno, "cannot stat %s", fullname); + rcsdate = date_from_time_t (sb.st_mtime); + date_to_internet (netdate, rcsdate); + free (rcsdate); + + send_to_server ("Checkin-time ", 0); + send_to_server (netdate, 0); + send_to_server ("\012", 1); + } + else + error (0, 0, + "warning: ignoring -d option due to server limitations"); + } + send_modified (vfile, fullname, &vers); + if (vers.options != NULL) + free (vers.options); + free (fullname); + return 0; +} + + + +void +client_import_done (void) +{ + if (toplevel_repos == NULL) + /* + * This happens if we are not processing any files, + * or for checkouts in directories without any existing stuff + * checked out. The following assignment is correct for the + * latter case; I don't think toplevel_repos matters for the + * former. + */ + /* FIXME: "can't happen" now that we call client_import_setup + at the beginning. */ + toplevel_repos = xstrdup (current_parsed_root->directory); + send_repository ("", toplevel_repos, "."); +} + + + +static void +notified_a_file (char *data, List *ent_list, char *short_pathname, + char *filename) +{ + FILE *fp; + FILE *newf; + size_t line_len = 8192; + char *line = xmalloc (line_len); + char *cp; + int nread; + int nwritten; + char *p; + + fp = open_file (CVSADM_NOTIFY, "r"); + if (getline (&line, &line_len, fp) < 0) + { + if (feof (fp)) + error (0, 0, "cannot read %s: end of file", CVSADM_NOTIFY); + else + error (0, errno, "cannot read %s", CVSADM_NOTIFY); + goto error_exit; + } + cp = strchr (line, '\t'); + if (cp == NULL) + { + error (0, 0, "malformed %s file", CVSADM_NOTIFY); + goto error_exit; + } + *cp = '\0'; + if (strcmp (filename, line + 1) != 0) + { + error (0, 0, "protocol error: notified %s, expected %s", filename, + line + 1); + } + + if (getline (&line, &line_len, fp) < 0) + { + if (feof (fp)) + { + free (line); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", CVSADM_NOTIFY); + if ( CVS_UNLINK (CVSADM_NOTIFY) < 0) + error (0, errno, "cannot remove %s", CVSADM_NOTIFY); + return; + } + else + { + error (0, errno, "cannot read %s", CVSADM_NOTIFY); + goto error_exit; + } + } + newf = open_file (CVSADM_NOTIFYTMP, "w"); + if (fputs (line, newf) < 0) + { + error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP); + goto error2; + } + while ((nread = fread (line, 1, line_len, fp)) > 0) + { + p = line; + while ((nwritten = fwrite (p, 1, nread, newf)) > 0) + { + nread -= nwritten; + p += nwritten; + } + if (ferror (newf)) + { + error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP); + goto error2; + } + } + if (ferror (fp)) + { + error (0, errno, "cannot read %s", CVSADM_NOTIFY); + goto error2; + } + if (fclose (newf) < 0) + { + 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); + return; + } + + { + /* In this case, we want rename_file() to ignore noexec. */ + int saved_noexec = noexec; + noexec = 0; + rename_file (CVSADM_NOTIFYTMP, CVSADM_NOTIFY); + noexec = saved_noexec; + } + + return; + error2: + (void)fclose (newf); + error_exit: + free (line); + (void)fclose (fp); +} + + + +static void +handle_notified (char *args, int len) +{ + call_in_directory (args, notified_a_file, NULL); +} + + + +void +client_notify (const char *repository, const char *update_dir, + const char *filename, int notif_type, const char *val) +{ + char buf[2]; + + send_a_repository ("", repository, update_dir); + send_to_server ("Notify ", 0); + send_to_server (filename, 0); + send_to_server ("\012", 1); + buf[0] = notif_type; + buf[1] = '\0'; + send_to_server (buf, 1); + send_to_server ("\t", 1); + send_to_server (val, 0); +} + + + +/* + * Send an option with an argument, dealing correctly with newlines in + * the argument. If ARG is NULL, forget the whole thing. + */ +void +option_with_arg (char *option, char *arg) +{ + if (arg == NULL) + return; + + send_to_server ("Argument ", 0); + send_to_server (option, 0); + send_to_server ("\012", 1); + + send_arg (arg); +} + + + +/* Send a date to the server. The input DATE is in RCS format. + The time will be GMT. + + We then convert that to the format required in the protocol + (including the "-D" option) and send it. According to + cvsclient.texi, RFC 822/1123 format is preferred. */ +void +client_senddate (const char *date) +{ + char buf[MAXDATELEN]; + + date_to_internet (buf, (char *)date); + option_with_arg ("-D", buf); +} + + + +void +send_init_command (void) +{ + /* This is here because we need the current_parsed_root->directory variable. */ + send_to_server ("init ", 0); + send_to_server (current_parsed_root->directory, 0); + send_to_server ("\012", 0); +} + +#endif /* CLIENT_SUPPORT */ diff --git a/contrib/cvs-1.12.9/src/client.h b/contrib/cvs-1.12.9/src/client.h new file mode 100644 index 0000000000..e493ee7cde --- /dev/null +++ b/contrib/cvs-1.12.9/src/client.h @@ -0,0 +1,197 @@ +/* Interface between the client and the rest of CVS. */ + +/* Stuff shared with the server. */ +extern char *mode_to_string (mode_t); +extern int change_mode (char *, char *, int); + +extern int gzip_level; +extern int file_gzip_level; + +struct buffer; + +void make_bufs_from_fds ( int, int, int, + struct buffer **, + struct buffer **, + int ); + + +#if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) + +/* Whether the connection should be encrypted. */ +extern int cvsencrypt; + +/* Whether the connection should use per-packet authentication. */ +extern int cvsauthenticate; + +# 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. + */ +extern int server_started; + +/* Is the -P option to checkout or update specified? */ +extern int client_prune_dirs; + +# ifdef AUTH_CLIENT_SUPPORT +extern int use_authenticating_server; +# endif /* AUTH_CLIENT_SUPPORT */ +# if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) +void connect_to_pserver (cvsroot_t *, struct buffer **, struct buffer **, + int, int ); +# ifndef CVS_AUTH_PORT +# define CVS_AUTH_PORT 2401 +# endif /* CVS_AUTH_PORT */ +# ifndef CVS_PROXY_PORT +# define CVS_PROXY_PORT 8080 +# endif /* CVS_AUTH_PORT */ +# endif /* (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) */ + +# if HAVE_KERBEROS +# ifndef CVS_PORT +# define CVS_PORT 1999 +# endif +# endif /* HAVE_KERBEROS */ + +/* Talking to the server. */ +void send_to_server (const char *str, size_t len); +void read_from_server (char *buf, size_t len); + +/* Internal functions that handle client communication to server, etc. */ +int supported_request (char *); +void option_with_arg (char *option, char *arg); + +/* Get the responses and then close the connection. */ +extern int get_responses_and_close (void); + +extern int get_server_responses (void); + +/* Start up the connection to the server on the other end. */ +void +start_server (void); + +/* Send the names of all the argument files to the server. */ +void +send_file_names (int argc, char **argv, unsigned int flags); + +/* Flags for send_file_names. */ +/* Expand wild cards? */ +# define SEND_EXPAND_WILD 1 + +/* + * 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). + */ +void +send_files (int argc, char **argv, int local, int aflag, + unsigned int flags); + +/* Flags for send_files. */ +# define SEND_BUILD_DIRS 1 +# define SEND_FORCE 2 +# define SEND_NO_CONTENTS 4 +# define BACKUP_MODIFIED_FILES 8 + +/* Send an argument to the remote server. */ +void +send_arg (char *string); + +/* Send a string of single-char options to the remote server, one by one. */ +void +send_option_string (char *string); + +extern void send_a_repository (const char *, const char *, const char *); + +#endif /* CLIENT_SUPPORT */ + +/* + * This structure is used to catalog the responses the client is + * prepared to see from the server. + */ + +struct response +{ + /* Name of the response. */ + char *name; + +#ifdef CLIENT_SUPPORT + /* + * Function to carry out the response. ARGS is the text of the + * command after name and, if present, a single space, have been + * stripped off. The function can scribble into ARGS if it wants. + * Note that although LEN is given, ARGS is also guaranteed to be + * '\0' terminated. + */ + void (*func) (char *args, int len); + + /* + * ok and error are special; they indicate we are at the end of the + * responses, and error indicates we should exit with nonzero + * exitstatus. + */ + enum {response_type_normal, response_type_ok, response_type_error} type; +#endif + + /* Used by the server to indicate whether response is supported by + the client, as set by the Valid-responses request. */ + enum { + /* + * Failure to implement this response can imply a fatal + * error. This should be set only for responses which were in the + * original version of the protocol; it should not be set for new + * responses. + */ + rs_essential, + + /* Some clients might not understand this response. */ + rs_optional, + + /* + * Set by the server to one of the following based on what this + * client actually supports. + */ + rs_supported, + rs_not_supported + } status; +}; + +/* Table of responses ending in an entry with a NULL name. */ + +extern struct response responses[]; + +#ifdef CLIENT_SUPPORT + +extern void client_senddate (const char *date); +extern void client_expand_modules (int argc, char **argv, int local); +extern void client_send_expansions (int local, char *where, + int build_dirs); +extern void client_nonexpanded_setup (void); + +extern void send_init_command (void); + +extern char **failed_patches; +extern int failed_patches_count; +extern char *toplevel_wd; +extern void client_import_setup (char *repository); +extern int client_process_import_file + (char *message, char *vfile, char *vtag, int targc, char *targv[], + char *repository, int all_files_binary, int modtime); +extern void client_import_done (void); +extern void client_notify (const char *, const char *, const char *, int, + const char *); +#endif /* CLIENT_SUPPORT */ diff --git a/contrib/cvs-1.12.9/src/commit.c b/contrib/cvs-1.12.9/src/commit.c new file mode 100644 index 0000000000..ec0e860552 --- /dev/null +++ b/contrib/cvs-1.12.9/src/commit.c @@ -0,0 +1,2333 @@ +/* + * 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 source distribution. + * + * Commit Files + * + * "commit" commits the present version to the RCS repository, AFTER + * having done a test on conflicts. + * + * The call is: cvs commit [options] files... + * + */ + +#include "cvs.h" +#include "getline.h" +#include "edit.h" +#include "fileattr.h" +#include "hardlink.h" + +static Dtype check_direntproc (void *callerdat, const char *dir, + const char *repos, const char *update_dir, + List *entries); +static int check_fileproc (void *callerdat, struct file_info *finfo); +static int check_filesdoneproc (void *callerdat, int err, const char *repos, + const char *update_dir, List *entries); +static int checkaddfile (const char *file, const char *repository, + const char *tag, const char *options, + RCSNode **rcsnode); +static Dtype commit_direntproc (void *callerdat, const char *dir, + const char *repos, const char *update_dir, + List *entries); +static int commit_dirleaveproc (void *callerdat, const char *dir, int err, + const char *update_dir, List *entries); +static int commit_fileproc (void *callerdat, struct file_info *finfo); +static int commit_filesdoneproc (void *callerdat, int err, + const char *repository, + const char *update_dir, List *entries); +static int finaladd (struct file_info *finfo, char *revision, char *tag, + char *options); +static int findmaxrev (Node * p, void *closure); +static int lock_RCS (const char *user, RCSNode *rcs, const char *rev, + const char *repository); +static int precommit_list_to_args_proc (Node * p, void *closure); +static int precommit_proc (const char *repository, const char *filter, + void *closure); +static int remove_file (struct file_info *finfo, char *tag, + char *message); +static void fixaddfile (const char *rcs); +static void fixbranch (RCSNode *, char *branch); +static void unlockrcs (RCSNode *rcs); +static void ci_delproc (Node *p); +static void masterlist_delproc (Node *p); + +struct commit_info +{ + Ctype status; /* as returned from Classify_File() */ + char *rev; /* a numeric rev, if we know it */ + char *tag; /* any sticky tag, or -r option */ + char *options; /* Any sticky -k option */ +}; +struct master_lists +{ + List *ulist; /* list for Update_Logfile */ + List *cilist; /* list with commit_info structs */ +}; + +static int force_ci = 0; +static int got_message; +static int aflag; +static char *saved_tag; +static char *write_dirtag; +static int write_dirnonbranch; +static char *logfile; +static List *mulist; +static char *saved_message; +static time_t last_register_time; + +static const char *const commit_usage[] = +{ + "Usage: %s %s [-Rlf] [-m msg | -F logfile] [-r rev] files...\n", + " -R Process directories recursively.\n", + " -l Local directory only (not recursive).\n", + " -f Force the file to be committed; disables recursion.\n", + " -F logfile Read the log message from file.\n", + " -m msg Log message.\n", + " -r rev Commit to this branch or trunk revision.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +#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. */ + const 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; +}; + + + +static Dtype +find_dirent_proc (void *callerdat, const char *dir, const char *repository, + const char *update_dir, List *entries) +{ + struct find_data *find_data = (struct find_data *)callerdat; + + /* This check seems to slowly be creeping throughout CVS (update + and send_dirent_proc by CVS 1.5, diff in 31 Oct 1995. My guess + is that it (or some variant thereof) should go in all the + dirent procs. Unless someone has some better idea... */ + if (!isdir (dir)) + return R_SKIP_ALL; + + /* initialize the ignore list for this directory */ + find_data->ignlist = getlist (); + + /* Print the same warm fuzzy as in check_direntproc, since that + code will never be run during client/server operation and we + want the messages to match. */ + if (!quiet) + error (0, 0, "Examining %s", update_dir); + + 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 (const char *file, const 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 (void *callerdat, int err, const char *repository, + const 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; +} + + + +/* 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 (void *callerdat, struct file_info *finfo) +{ + Vers_TS *vers; + enum classify_type status; + Node *node; + 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); + } + + xfinfo = *finfo; + xfinfo.repository = NULL; + xfinfo.rcs = NULL; + + vers = Version_TS (&xfinfo, NULL, saved_tag, NULL, 0, 0); + if (vers->vn_user == NULL) + { + if (vers->ts_user == NULL) + error (0, 0, "nothing known about `%s'", finfo->fullname); + else + error (0, 0, "use `%s add' to create an entry for `%s'", + program_name, finfo->fullname); + freevers_ts (&vers); + return 1; + } + if (vers->vn_user[0] == '-') + { + if (vers->ts_user != NULL) + { + error (0, 0, + "`%s' should be removed and is still there (or is back" + " again)", finfo->fullname); + freevers_ts (&vers); + return 1; + } + /* else */ + status = T_REMOVED; + } + else if (strcmp (vers->vn_user, "0") == 0) + { + if (vers->ts_user == NULL) + { + /* This happens when one has `cvs add'ed a file, but it no + longer exists in the working directory at commit time. + FIXME: What classify_file does in this case is print + "new-born %s has disappeared" and removes the entry. + We probably should do the same. */ + if (!really_quiet) + error (0, 0, "warning: new-born %s has disappeared", + finfo->fullname); + status = T_REMOVE_ENTRY; + } + else + status = T_ADDED; + } + else if (vers->ts_user == NULL) + { + /* FIXME: What classify_file does in this case is print + "%s was lost". We probably should do the same. */ + freevers_ts (&vers); + return 0; + } + else if (vers->ts_rcs != NULL + && (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 + { + /* This covers unmodified files, as well as a variety of other + cases. FIXME: we probably should be printing a message and + returning 1 for many of those cases (but I'm not sure + exactly which ones). */ + freevers_ts (&vers); + return 0; + } + + 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 = data; + (void)addnode (args->ulist, node); + + ++args->argc; + + freevers_ts (&vers); + return 0; +} + + + +static int +copy_ulist (Node *node, void *data) +{ + struct find_data *args = (struct find_data *)data; + args->argv[args->argc++] = node->key; + return 0; +} +#endif /* CLIENT_SUPPORT */ + + + +#ifdef SERVER_SUPPORT +# define COMMIT_OPTIONS "+nlRm:fF:r:" +#else /* !SERVER_SUPPORT */ +# define COMMIT_OPTIONS "+lRm:fF:r:" +#endif /* SERVER_SUPPORT */ +int +commit (int argc, char **argv) +{ + int c; + int err = 0; + int local = 0; + + if (argc == -1) + usage (commit_usage); + +#ifdef CVS_BADROOT + /* + * For log purposes, do not allow "root" to commit files. If you look + * like root, but are really logged in as a non-root user, it's OK. + */ + /* FIXME: Shouldn't this check be much more closely related to the + readonly user stuff (CVSROOT/readers, &c). That is, why should + root be able to "cvs init", "cvs import", &c, but not "cvs ci"? */ + if (geteuid () == (uid_t) 0 +# ifdef CLIENT_SUPPORT + /* Who we are on the client side doesn't affect logging. */ + && !current_parsed_root->isremote +# endif + ) + { + struct passwd *pw; + + if ((pw = getpwnam (getcaller ())) == NULL) + error (1, 0, + "your apparent username (%s) is unknown to this system", + getcaller ()); + if (pw->pw_uid == (uid_t) 0) + error (1, 0, "'root' is not allowed to commit files"); + } +#endif /* CVS_BADROOT */ + + optind = 0; + while ((c = getopt (argc, argv, COMMIT_OPTIONS)) != -1) + { + switch (c) + { +#ifdef SERVER_SUPPORT + case 'n': + /* Silently ignore -n for compatibility with old + * clients. + */ + break; +#endif /* SERVER_SUPPORT */ + case 'm': +#ifdef FORCE_USE_EDITOR + use_editor = 1; +#else + use_editor = 0; +#endif + if (saved_message) + { + free (saved_message); + saved_message = NULL; + } + + saved_message = xstrdup(optarg); + break; + case 'r': + if (saved_tag) + free (saved_tag); + saved_tag = xstrdup (optarg); + break; + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case 'f': + force_ci = 1; + local = 1; /* also disable recursion */ + break; + case 'F': +#ifdef FORCE_USE_EDITOR + use_editor = 1; +#else + use_editor = 0; +#endif + logfile = optarg; + break; + case '?': + default: + usage (commit_usage); + break; + } + } + argc -= optind; + argv += optind; + + /* numeric specified revision means we ignore sticky tags... */ + if (saved_tag && isdigit ((unsigned char) *saved_tag)) + { + char *p = saved_tag + strlen (saved_tag); + aflag = 1; + /* strip trailing dots and leading zeros */ + while (*--p == '.') ; + p[1] = '\0'; + while (saved_tag[0] == '0' && isdigit ((unsigned char) saved_tag[1])) + ++saved_tag; + } + + /* some checks related to the "-F logfile" option */ + if (logfile) + { + size_t size = 0, len; + + if (saved_message) + error (1, 0, "cannot specify both a message and a log file"); + + get_file (logfile, logfile, "r", &saved_message, &size, &len); + } + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + struct find_data find_args; + + ign_setup (); + + find_args.ulist = getlist (); + find_args.argc = 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 || saved_tag != NULL; + + err = start_recursion + (find_fileproc, find_filesdoneproc, find_dirent_proc, NULL, + &find_args, argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE, + NULL, 0, NULL ); + 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). */ + dellist (&find_args.ulist); + return 0; + } + + /* Now we keep track of which files we actually are going to + operate on, and only work with those files in the future. + This saves time--we don't want to search the file system + of the working directory twice. */ + if (size_overflow_p (xtimes (find_args.argc, sizeof (char **)))) + { + find_args.argc = 0; + return 0; + } + find_args.argv = xmalloc (xtimes (find_args.argc, sizeof (char **))); + 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.). + + 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 (); + + /* + * We do this once, not once for each directory as in normal CVS. + * The protocol is designed this way. This is a feature. + */ + if (use_editor) + do_editor (".", &saved_message, NULL, find_args.ulist); + + /* We always send some sort of message, even if empty. */ + option_with_arg ("-m", saved_message ? saved_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) + send_arg("-f"); + option_with_arg ("-r", saved_tag); + send_arg ("--"); + + /* 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 the "ci" + program, which we used to call, wanted the file to exist, + then it would be relatively simple to fix in the server. */ + send_files (find_args.argc, find_args.argv, local, 0, + find_args.force ? SEND_FORCE : 0); + + /* Sending only the names of the files which were modified, added, + or removed means that the server will only do an up-to-date + check on those files. This is different from local CVS and + 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); + free (find_args.argv); + dellist (&find_args.ulist); + + send_to_server ("ci\012", 0); + err = get_responses_and_close (); + if (err != 0 && use_editor && saved_message != NULL) + { + /* If there was an error, don't nuke the user's carefully + constructed prose. This is something of a kludge; a better + solution is probably more along the lines of #150 in TODO + (doing a second up-to-date check before accepting the + log message has also been suggested, but that seems kind of + iffy because the real up-to-date check could still fail, + another error could occur, &c. Also, a second check would + slow things down). */ + + char *fname; + FILE *fp; + + fp = cvs_temp_file (&fname); + if (fp == NULL) + error (1, 0, "cannot create temporary file %s", fname); + if (fwrite (saved_message, 1, strlen (saved_message), fp) + != strlen (saved_message)) + error (1, errno, "cannot write temporary file %s", fname); + if (fclose (fp) < 0) + error (0, errno, "cannot close temporary file %s", fname); + error (0, 0, "saving log message in %s", fname); + free (fname); + } + return err; + } +#endif + + if (saved_tag != NULL) + tag_check_valid (saved_tag, argc, argv, local, aflag, ""); + + /* XXX - this is not the perfect check for this */ + if (argc <= 0) + write_dirtag = saved_tag; + + wrap_setup (); + + lock_tree_promotably (argc, argv, local, W_LOCAL, aflag); + + /* + * Set up the master update list and hard link list + */ + mulist = getlist (); + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms) + { + hardlist = getlist (); + + /* + * We need to save the working directory so that + * check_fileproc can construct a full pathname for each file. + */ + working_dir = xgetwd(); + } +#endif + + /* + * Run the recursion processor to verify the files are all up-to-date + */ + err = start_recursion (check_fileproc, check_filesdoneproc, + check_direntproc, NULL, NULL, argc, argv, local, + W_LOCAL, aflag, CVS_LOCK_NONE, NULL, 1, NULL); + if (err) + 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, NULL, + argc, argv, local, W_LOCAL, aflag, + CVS_LOCK_WRITE, NULL, 1, NULL); + + /* + * Unlock all the dirs and clean up + */ + Lock_Cleanup (); + dellist (&mulist); + + /* see if we need to sleep before returning to avoid time-stamp races */ + if ( +#ifdef SERVER_SUPPORT + /* But only sleep on the client. */ + !server_active && +#endif + last_register_time + ) + sleep_past (last_register_time); + + return err; +} + + + +/* 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 (struct file_info *finfo, Vers_TS **vers) +{ + int save_noexec, save_quiet, save_really_quiet; + Ctype status; + + /* FIXME: Do we need to save quiet as well as really_quiet? Last + time I glanced at Classify_File I only saw it looking at really_quiet + not quiet. */ + save_noexec = noexec; + save_quiet = quiet; + save_really_quiet = really_quiet; + noexec = quiet = really_quiet = 1; + + /* handle specified numeric revision specially */ + if (saved_tag && isdigit ((unsigned char) *saved_tag)) + { + /* If the tag is for the trunk, make sure we're at the head */ + if (numdots (saved_tag) < 2) + { + 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, saved_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) + status = T_MODIFIED; + else + status = xstatus; + } + } + else + { + char *xtag, *cp; + + /* + * The revision is off the main trunk; make sure we're + * up-to-date with the head of the specified branch. + */ + xtag = xstrdup (saved_tag); + if ((numdots (xtag) & 1) != 0) + { + cp = strrchr (xtag, '.'); + *cp = '\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, 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 (saved_tag); + free (xtag); + } + } + else + status = Classify_File (finfo, saved_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 (void *callerdat, struct file_info *finfo) +{ + Ctype status; + const char *xdir; + Node *p; + List *ulist, *cilist; + Vers_TS *vers; + struct commit_info *ci; + struct logfile_info *li; + int retval = 1; + + size_t cvsroot_len = strlen (current_parsed_root->directory); + + if (!finfo->repository) + { + error (0, 0, "nothing known about `%s'", finfo->fullname); + return 1; + } + + if (strncmp (finfo->repository, current_parsed_root->directory, + cvsroot_len) == 0 + && ISSLASH (finfo->repository[cvsroot_len]) + && strncmp (finfo->repository + cvsroot_len + 1, + CVSROOTADM, + sizeof (CVSROOTADM) - 1) == 0 + && ISSLASH (finfo->repository[cvsroot_len + sizeof (CVSROOTADM)]) + && strcmp (finfo->repository + cvsroot_len + sizeof (CVSROOTADM) + 1, + CVSNULLREPOS) == 0 + ) + error (1, 0, "cannot check in to %s", finfo->repository); + + 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 + * it will be committed. + */ + if (force_ci && status == T_UPTODATE) + status = T_MODIFIED; + + switch (status) + { + case T_CHECKOUT: + case T_PATCH: + case T_NEEDS_MERGE: + case T_CONFLICT: + case T_REMOVE_ENTRY: + error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname); + goto out; + case T_MODIFIED: + case T_ADDED: + case T_REMOVED: + /* + * some quick sanity checks; if no numeric -r option specified: + * - can't have a sticky date + * - can't have a sticky tag that is not a branch + * Also, + * - if status is T_REMOVED, file must not exist and its entry + * can't have a numeric sticky tag. + * - if status is T_ADDED, rcs file must not exist unless on + * a branch or head is dead + * - 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 + * an RCS_MERGE_PAT in the file. + */ + if (!saved_tag || !isdigit ((unsigned char) *saved_tag)) + { + if (vers->date) + { + error (0, 0, + "cannot commit with sticky date for file `%s'", + finfo->fullname); + goto out; + } + if (status == T_MODIFIED && vers->tag && + !RCS_isbranch (finfo->rcs, vers->tag)) + { + error (0, 0, + "sticky tag `%s' for file `%s' is not a branch", + vers->tag, finfo->fullname); + goto out; + } + } + if (status == T_MODIFIED && !force_ci && vers->ts_conflict) + { + /* + * We found a "conflict" marker. + * + * If the timestamp on the file is the same as the + * timestamp stored in the Entries file, we block the commit. + */ + if ( file_has_conflict ( finfo, vers->ts_conflict ) ) + { + error (0, 0, + "file `%s' had a conflict and has not been modified", + finfo->fullname); + goto out; + } + + if (file_has_markers (finfo)) + { + /* Make this a warning, not an error, because we have + no way of knowing whether the "conflict indicators" + are really from a conflict or whether they are part + of the document itself (cvs.texinfo and sanity.sh in + CVS itself, for example, tend to want to have strings + like ">>>>>>>" at the start of a line). Making people + kludge this the way they need to kludge keyword + expansion seems undesirable. And it is worse than + keyword expansion, because there is no -ko + analogue. */ + error (0, 0, + "\ +warning: file `%s' seems to still contain conflict indicators", + finfo->fullname); + } + } + + if (status == T_REMOVED) + { + if (vers->ts_user != NULL) + { + error (0, 0, + "`%s' should be removed and is still there (or is" + " back again)", finfo->fullname); + goto out; + } + + if (vers->tag && isdigit ((unsigned char) *vers->tag)) + { + /* Remove also tries to forbid this, but we should check + here. I'm only _sure_ about somewhat obscure cases + (hacking the Entries file, using an old version of + CVS for the remove and a new one for the commit), but + there might be other cases. */ + error (0, 0, + "cannot remove file `%s' which has a numeric sticky" + " tag of `%s'", finfo->fullname, vers->tag); + freevers_ts (&vers); + goto out; + } + } + if (status == T_ADDED) + { + if (vers->tag == NULL) + { + if (finfo->rcs != NULL && + !RCS_isdead (finfo->rcs, finfo->rcs->head)) + { + error (0, 0, + "cannot add file `%s' when RCS file `%s' already exists", + finfo->fullname, finfo->rcs->path); + goto out; + } + } + else if (isdigit ((unsigned char) *vers->tag) && + numdots (vers->tag) > 1) + { + error (0, 0, + "cannot add file `%s' with revision `%s'; must be on trunk", + finfo->fullname, vers->tag); + goto out; + } + } + + /* done with consistency checks; now, to get on with the commit */ + if (finfo->update_dir[0] == '\0') + xdir = "."; + else + xdir = finfo->update_dir; + if ((p = findnode (mulist, xdir)) != NULL) + { + ulist = ((struct master_lists *) p->data)->ulist; + cilist = ((struct master_lists *) p->data)->cilist; + } + else + { + struct master_lists *ml; + + ml = (struct master_lists *) + xmalloc( sizeof( struct master_lists ) ); + ulist = ml->ulist = getlist(); + cilist = ml->cilist = getlist(); + + p = getnode (); + p->key = xstrdup (xdir); + p->type = UPDATE; + p->data = ml; + p->delproc = masterlist_delproc; + (void) addnode (mulist, p); + } + + /* first do ulist, then cilist */ + p = getnode (); + p->key = xstrdup (finfo->file); + p->type = UPDATE; + p->delproc = update_delproc; + 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 = li; + (void) addnode (ulist, p); + + p = getnode (); + p->key = xstrdup (finfo->file); + p->type = UPDATE; + p->delproc = ci_delproc; + ci = (struct commit_info *) xmalloc (sizeof (struct commit_info)); + ci->status = status; + if (vers->tag) + if (isdigit ((unsigned char) *vers->tag)) + ci->rev = xstrdup (vers->tag); + else + ci->rev = RCS_whatbranch (finfo->rcs, vers->tag); + else + ci->rev = (char *) NULL; + ci->tag = xstrdup (vers->tag); + ci->options = xstrdup(vers->options); + p->data = ci; + (void) addnode (cilist, p); + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms) + { + /* Add this file to hardlist, indexed on its inode. When + we are done, we can find out what files are hardlinked + to a given file by looking up its inode in hardlist. */ + char *fullpath; + Node *linkp; + struct hardlink_info *hlinfo; + + /* Get the full pathname of the current file. */ + fullpath = xmalloc (strlen(working_dir) + + strlen(finfo->fullname) + 2); + sprintf (fullpath, "%s/%s", working_dir, finfo->fullname); + + /* To permit following links in subdirectories, files + are keyed on finfo->fullname, not on finfo->name. */ + linkp = lookup_file_by_inode (fullpath); + + /* If linkp is NULL, the file doesn't exist... maybe + we're doing a remove operation? */ + if (linkp != NULL) + { + /* Create a new hardlink_info node, which will record + the current file's status and the links listed in its + `hardlinks' delta field. We will append this + hardlink_info node to the appropriate hardlist entry. */ + hlinfo = (struct hardlink_info *) + xmalloc (sizeof (struct hardlink_info)); + hlinfo->status = status; + linkp->data = hlinfo; + } + } +#endif + + break; + case T_UNKNOWN: + error (0, 0, "nothing known about `%s'", finfo->fullname); + goto out; + case T_UPTODATE: + break; + default: + error (0, 0, "CVS internal error: unknown status %d", status); + break; + } + + retval = 0; + + out: + + freevers_ts (&vers); + return retval; +} + + + +/* + * By default, return the code that tells do_recursion to examine all + * directories + */ +/* ARGSUSED */ +static Dtype +check_direntproc (void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries) +{ + if (!isdir (dir)) + return R_SKIP_ALL; + + if (!quiet) + error (0, 0, "Examining %s", update_dir); + + return R_PROCESS; +} + + + +/* + * Walklist proc to generate an arg list from the line in commitinfo + */ +static int +precommit_list_to_args_proc (p, closure) + Node *p; + void *closure; +{ + struct format_cmdline_walklist_closure *c = closure; + struct logfile_info *li; + char *arg = NULL; + const char *f; + char *d; + size_t doff; + + if (p->data == NULL) return 1; + + f = c->format; + d = *c->d; + /* foreach requested attribute */ + while (*f) + { + switch (*f++) + { + case 's': + li = p->data; + if (li->type == T_ADDED + || li->type == T_MODIFIED + || li->type == T_REMOVED) + { + arg = p->key; + } + break; + default: + error (1, 0, + "Unknown format character or not a list attribute: %c", + f[-1]); + /* NOTREACHED */ + break; + } + /* copy the attribute into an argument */ + if (c->quotes) + { + arg = cmdlineescape (c->quotes, arg); + } + else + { + arg = cmdlinequote ('"', arg); + } + doff = d - *c->buf; + expand_string (c->buf, c->length, doff + strlen (arg)); + d = *c->buf + doff; + strncpy (d, arg, strlen (arg)); + d += strlen (arg); + free (arg); + + /* and always put the extra space on. we'll have to back up a char + * when we're done, but that seems most efficient + */ + doff = d - *c->buf; + expand_string (c->buf, c->length, doff + 1); + d = *c->buf + doff; + *d++ = ' '; + } + /* correct our original pointer into the buff */ + *c->d = d; + return 0; +} + + + +/* + * Callback proc for pre-commit checking + */ +static int +precommit_proc (const char *repository, const char *filter, void *closure) +{ + char *newfilter = NULL; + char *cmdline; + const char *srepos = Short_Repository (repository); + List *ulist = (List *)closure; + +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (!strchr (filter, '%')) + { + error (0, 0, + "warning: commitinfo line contains no format strings:\n" + " \"%s\"\n" + "Appending defaults (\" %%r/%%p %%s\"), but please be aware that this usage is\n" + "deprecated.", filter); + newfilter = xmalloc (strlen (filter) + 10); + strcpy (newfilter, filter); + strcat (newfilter, " %r/%p %s"); + filter = newfilter; + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + + cmdline = format_cmdline ( +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + 0, srepos, +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + filter, + "p", "s", srepos, + "r", "s", current_parsed_root->directory, + "s", ",", ulist, precommit_list_to_args_proc, (void *) NULL, + (char *)NULL + ); + + if (newfilter) free (newfilter); + + if (!cmdline || !strlen (cmdline)) + { + if (cmdline) free (cmdline); + error (0, 0, "precommit proc resolved to the empty string!"); + return 1; + } + + run_setup (cmdline); + free (cmdline); + + return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY); +} + + + +/* + * Run the pre-commit checks for the dir + */ +/* ARGSUSED */ +static int +check_filesdoneproc (void *callerdat, int err, const char *repos, + const char *update_dir, List *entries) +{ + int n; + Node *p; + List *saved_ulist; + + /* find the update list for this dir */ + p = findnode (mulist, update_dir); + if (p != NULL) + saved_ulist = ((struct master_lists *) p->data)->ulist; + else + saved_ulist = NULL; + + /* skip the checks if there's nothing to do */ + if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list) + return err; + + /* run any pre-commit checks */ + n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, PIOPT_ALL, + saved_ulist); + if (n > 0) + { + error (0, 0, "Pre-commit check failed"); + err += n; + } + + return err; +} + + + +/* + * Do the work of committing a file + */ +static int maxrev; +static char *sbranch; + +/* ARGSUSED */ +static int +commit_fileproc (void *callerdat, struct file_info *finfo) +{ + Node *p; + int err = 0; + List *ulist, *cilist; + struct commit_info *ci; + + /* 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, "."); + else + p = findnode (mulist, finfo->update_dir); + + /* + * if p is null, there were file type command line args which were + * all up-to-date so nothing really needs to be done + */ + if (p == NULL) + return 0; + ulist = ((struct master_lists *) p->data)->ulist; + cilist = ((struct master_lists *) p->data)->cilist; + + /* + * At this point, we should have the commit message unless we were called + * with files as args from the command line. In that latter case, we + * need to get the commit message ourselves + */ + if (!got_message) + { + got_message = 1; + if ( +#ifdef SERVER_SUPPORT + !server_active && +#endif + use_editor) + do_editor (finfo->update_dir, &saved_message, + finfo->repository, ulist); + do_verify (&saved_message, finfo->repository); + } + + p = findnode (cilist, finfo->file); + if (p == NULL) + return 0; + + ci = p->data; + if (ci->status == T_MODIFIED) + { + 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->rcs); + err = 1; + goto out; + } + } + else if (ci->status == T_ADDED) + { + if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options, + &finfo->rcs) != 0) + { + if (finfo->rcs != NULL) + fixaddfile (finfo->rcs->path); + err = 1; + goto out; + } + + /* adding files with a tag, now means adding them on a branch. + Since the branch test was done in check_fileproc for + modified files, we need to stub it in again here. */ + + if (ci->tag + + /* If numeric, it is on the trunk; check_fileproc enforced + this. */ + && !isdigit ((unsigned char) ci->tag[0])) + { + if (finfo->rcs == NULL) + error (1, 0, "internal error: no parsed RCS file"); + if (ci->rev) + free (ci->rev); + ci->rev = RCS_whatbranch (finfo->rcs, ci->tag); + err = Checkin ('A', finfo, ci->rev, + ci->tag, ci->options, saved_message); + if (err != 0) + { + unlockrcs (finfo->rcs); + fixbranch (finfo->rcs, sbranch); + } + + (void) time (&last_register_time); + + ci->status = T_UPTODATE; + } + } + + /* + * Add the file for real + */ + if (ci->status == T_ADDED) + { + char *xrev = (char *) NULL; + + if (ci->rev == NULL) + { + /* find the max major rev number in this directory */ + maxrev = 0; + (void) walklist (finfo->entries, findmaxrev, NULL); + if (finfo->rcs->head) { + /* resurrecting: include dead revision */ + int thisrev = atoi (finfo->rcs->head); + if (thisrev > maxrev) + maxrev = thisrev; + } + if (maxrev == 0) + maxrev = 1; + xrev = xmalloc (20); + (void) sprintf (xrev, "%d", maxrev); + } + + /* XXX - an added file with symbolic -r should add tag as well */ + err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options); + if (xrev) + free (xrev); + } + else if (ci->status == T_MODIFIED) + { + err = Checkin ('M', finfo, ci->rev, ci->tag, + ci->options, saved_message); + + (void) time (&last_register_time); + + if (err != 0) + { + unlockrcs (finfo->rcs); + fixbranch (finfo->rcs, sbranch); + } + } + else if (ci->status == T_REMOVED) + { + err = remove_file (finfo, ci->tag, saved_message); +#ifdef SERVER_SUPPORT + if (server_active) { + server_scratch_entry_only (); + server_updated (finfo, + NULL, + + /* Doesn't matter, it won't get checked. */ + SERVER_UPDATED, + + (mode_t) -1, + (unsigned char *) NULL, + (struct buffer *) NULL); + } +#endif + } + + /* Clearly this is right for T_MODIFIED. I haven't thought so much + about T_ADDED or T_REMOVED. */ + notify_do ('C', finfo->file, getcaller (), NULL, NULL, finfo->repository); + +out: + if (err != 0) + { + /* on failure, remove the file from ulist */ + p = findnode (ulist, finfo->file); + 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 since 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 = p->data; + li->rev_new = xstrdup (vers->vn_rcs); + freevers_ts (&vers); + } + } + } + if (SIG_inCrSect ()) + SIG_endCrSect (); + + return err; +} + + + +/* + * Log the commit and clean up the update list + */ +/* ARGSUSED */ +static int +commit_filesdoneproc (void *callerdat, int err, const char *repository, + const char *update_dir, List *entries) +{ + Node *p; + List *ulist; + + p = findnode (mulist, update_dir); + if (p == NULL) + return err; + + ulist = ((struct master_lists *) p->data)->ulist; + + got_message = 0; + + Update_Logfile (repository, saved_message, (FILE *) 0, ulist); + + /* Build the administrative files if necessary. */ + { + const char *p; + + if (strncmp (current_parsed_root->directory, repository, + strlen (current_parsed_root->directory)) != 0) + error (0, 0, + "internal error: repository (%s) doesn't begin with root (%s)", + repository, current_parsed_root->directory); + p = repository + strlen (current_parsed_root->directory); + if (*p == '/') + ++p; + if (strcmp ("CVSROOT", p) == 0 + /* Check for subdirectories because people may want to create + subdirectories and list files therein in checkoutlist. */ + || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0 + ) + { + /* "Database" might a little bit grandiose and/or vague, + but "checked-out copies of administrative files, unless + in the case of modules and you are using ndbm in which + case modules.{pag,dir,db}" is verbose and excessively + focused on how the database is implemented. */ + + /* mkmodules requires the absolute name of the CVSROOT directory. + Remove anything after the `CVSROOT' component -- this is + necessary when committing in a subdirectory of CVSROOT. */ + char *admin_dir = xstrdup (repository); + int cvsrootlen = strlen ("CVSROOT"); + assert (admin_dir[p - repository + cvsrootlen] == '\0' + || admin_dir[p - repository + cvsrootlen] == '/'); + admin_dir[p - repository + cvsrootlen] = '\0'; + + if (!really_quiet) + { + cvs_output (program_name, 0); + cvs_output (" ", 1); + cvs_output (cvs_cmd_name, 0); + cvs_output (": Rebuilding administrative file database\n", 0); + } + mkmodules (admin_dir); + free (admin_dir); + WriteTemplate (".", 1, repository); + } + } + + return err; +} + + + +/* + * Get the log message for a dir + */ +/* ARGSUSED */ +static Dtype +commit_direntproc (void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries) +{ + Node *p; + List *ulist; + char *real_repos; + + if (!isdir (dir)) + return R_SKIP_ALL; + + /* find the update list for this dir */ + p = findnode (mulist, update_dir); + if (p != NULL) + ulist = ((struct master_lists *) p->data)->ulist; + else + ulist = (List *) NULL; + + /* skip the files as an optimization */ + if (ulist == NULL || ulist->list->next == ulist->list) + return R_SKIP_FILES; + + /* get commit message */ + got_message = 1; + real_repos = Name_Repository (dir, update_dir); + if ( +#ifdef SERVER_SUPPORT + !server_active && +#endif + use_editor) + do_editor (update_dir, &saved_message, real_repos, ulist); + do_verify (&saved_message, real_repos); + free (real_repos); + return R_PROCESS; +} + + + +/* + * Process the post-commit proc if necessary + */ +/* ARGSUSED */ +static int +commit_dirleaveproc (void *callerdat, const char *dir, int err, + const 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) + { + char *repos = Name_Repository (NULL, update_dir); + WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch, + update_dir, repos); + free (repos); + } + + return err; +} + + + +/* + * find the maximum major rev number in an entries file + */ +static int +findmaxrev (Node *p, void *closure) +{ + int thisrev; + Entnode *entdata = p->data; + + if (entdata->type != ENT_FILE) + return 0; + thisrev = atoi (entdata->version); + if (thisrev > maxrev) + maxrev = thisrev; + return 0; +} + +/* + * Actually remove a file by moving it to the attic + * XXX - if removing a ,v file that is a relative symbolic link to + * another ,v file, we probably should add a ".." component to the + * link to keep it relative after we move it into the attic. + + Return value is 0 on success, or >0 on error (in which case we have + printed an error message). */ +static int +remove_file (struct file_info *finfo, char *tag, char *message) +{ + int retcode; + + int branch; + int lockflag; + char *corev; + char *rev; + char *prev_rev; + char *old_path; + + corev = NULL; + rev = NULL; + prev_rev = NULL; + + retcode = 0; + + if (finfo->rcs == NULL) + error (1, 0, "internal error: no parsed RCS file"); + + branch = 0; + if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag))) + { + /* a symbolic tag is specified; just remove the tag from the file */ + if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0) + { + if (!quiet) + error (0, retcode == -1 ? errno : 0, + "failed to remove tag `%s' from `%s'", tag, + finfo->fullname); + return 1; + } + RCS_rewrite (finfo->rcs, NULL, NULL); + Scratch_Entry (finfo->entries, finfo->file); + return 0; + } + + /* we are removing the file from either the head or a branch */ + /* commit a new, dead revision. */ + + rev = NULL; + lockflag = 1; + if (branch) + { + char *branchname; + + rev = RCS_whatbranch (finfo->rcs, tag); + if (rev == NULL) + { + error (0, 0, "cannot find branch \"%s\".", tag); + return 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 (finfo->rcs, tag, 1, (int *) NULL); + prev_rev = xstrdup (corev); + lockflag = 0; + } else + { + corev = xstrdup (rev); + prev_rev = xstrdup (branchname); + free (branchname); + } + + } else /* Not a branch */ + { + /* Get current head revision of file. */ + 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 (finfo->rcs, NULL) != 0) + { + error (0, 0, "cannot change branch to default for %s", + finfo->fullname); + return 1; + } + RCS_rewrite (finfo->rcs, NULL, NULL); + } + + /* check something out. Generally this is the head. If we have a + 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) + { + error (0, 0, + "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) + { + if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0) + RCS_rewrite (finfo->rcs, NULL, NULL); + } + + if (corev != NULL) + free (corev); + + retcode = RCS_checkin (finfo->rcs, 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'", finfo->fullname); + return 1; + } + /* At this point, the file has been committed as removed. We should + probably tell the history file about it */ + history_write ('R', NULL, finfo->rcs->head, finfo->file, finfo->repository); + + if (rev != NULL) + free (rev); + + old_path = xstrdup (finfo->rcs->path); + if (!branch) + RCS_setattic (finfo->rcs, 1); + + /* Print message that file was removed. */ + if (!really_quiet) + { + 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 ("\n", 0); + } + + free (prev_rev); + + free (old_path); + + Scratch_Entry (finfo->entries, finfo->file); + return 0; +} + + + +/* + * Do the actual checkin for added files + */ +static int +finaladd (struct file_info *finfo, char *rev, char *tag, char *options) +{ + int ret; + + ret = Checkin ('A', finfo, rev, tag, options, saved_message); + if (ret == 0) + { + char *tmp = xmalloc (strlen (finfo->file) + sizeof (CVSADM) + + sizeof (CVSEXT_LOG) + 10); + (void) sprintf (tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); + if (unlink_file (tmp) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", tmp); + free (tmp); + } + else if (finfo->rcs != NULL) + fixaddfile (finfo->rcs->path); + + (void) time (&last_register_time); + + return ret; +} + + + +/* + * Unlock an rcs file + */ +static void +unlockrcs (RCSNode *rcs) +{ + int retcode; + + if ((retcode = RCS_unlock (rcs, NULL, 1)) != 0) + error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, + "could not unlock %s", rcs->path); + else + RCS_rewrite (rcs, NULL, NULL); +} + + + +/* + * remove a partially added file. if we can parse it, leave it alone. + * + * FIXME: Every caller that calls this function can access finfo->rcs (the + * parsed RCSNode data), so we should be able to detect that the file needs + * to be removed without reparsing the file as we do below. + */ +static void +fixaddfile (const char *rcs) +{ + RCSNode *rcsfile; + int save_really_quiet; + + save_really_quiet = really_quiet; + really_quiet = 1; + if ((rcsfile = RCS_parsercsfile (rcs)) == NULL) + { + if (unlink_file (rcs) < 0) + error (0, errno, "cannot remove %s", rcs); + } + else + freercsnode (&rcsfile); + really_quiet = save_really_quiet; +} + + + +/* + * put the branch back on an rcs file + */ +static void +fixbranch (RCSNode *rcs, char *branch) +{ + int retcode; + + if (branch != NULL) + { + 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->path); + RCS_rewrite (rcs, NULL, NULL); + } +} + + + +/* + * do the initial part of a file add for the named file. if adding + * with a tag, put the file in the Attic and point the symbolic tag + * at the committed revision. + * + * INPUTS + * file The name of the file in the workspace. + * repository The repository directory to expect to find FILE,v in. + * tag The name or rev num of the branch being added to, if any. + * options Any RCS keyword expansion options specified by the user. + * rcsnode A pointer to the pre-parsed RCSNode for this file, if the file + * exists in the repository. If this is NULL, assume the file + * does not yet exist. + * + * RETURNS + * 0 on success. + * 1 on errors, after printing any appropriate error messages. + * + * ERRORS + * This function will return an error when any of the following functions do: + * add_rcs_file + * RCS_setattic + * lock_RCS + * RCS_checkin + * RCS_parse (called to verify the newly created archive file) + * RCS_settag + */ + +static int +checkaddfile (const char *file, const char *repository, const char *tag, + const char *options, RCSNode **rcsnode) +{ + RCSNode *rcs; + char *fname; + int newfile = 0; /* Set to 1 if we created a new RCS archive. */ + int retval = 1; + int adding_on_branch; + + assert (rcsnode != NULL); + + /* Callers expect to be able to use either "" or NULL to mean the + default keyword expansion. */ + if (options != NULL && options[0] == '\0') + options = NULL; + if (options != NULL) + assert (options[0] == '-' && options[1] == 'k'); + + /* If numeric, it is on the trunk; check_fileproc enforced + this. */ + adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]); + + if (*rcsnode == NULL) + { + char *rcsname; + char *desc = NULL; + size_t descalloc = 0; + size_t desclen = 0; + const char *opt; + + if ( adding_on_branch ) + { + mode_t omask; + rcsname = xmalloc (strlen (repository) + + sizeof (CVSATTIC) + + strlen (file) + + sizeof (RCSEXT) + + 3); + (void) sprintf (rcsname, "%s/%s", repository, CVSATTIC); + omask = umask ( cvsumask ); + if (CVS_MKDIR (rcsname, 0777 ) != 0 && errno != EEXIST) + error (1, errno, "cannot make directory `%s'", rcsname); + (void) umask ( omask ); + (void) sprintf (rcsname, + "%s/%s/%s%s", + repository, + CVSATTIC, + file, + RCSEXT); + } + else + { + rcsname = xmalloc (strlen (repository) + + strlen (file) + + sizeof (RCSEXT) + + 2); + (void) sprintf (rcsname, + "%s/%s%s", + repository, + file, + RCSEXT); + } + + /* this is the first time we have ever seen this file; create + an RCS file. */ + 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)) + /* FIXME: Should be including update_dir in the appropriate + place here. */ + get_file (fname, fname, "r", &desc, &descalloc, &desclen); + free (fname); + + /* From reading the RCS 5.7 source, "rcs -i" adds a newline to the + end of the log message if the message is nonempty. + Do it. RCS also deletes certain whitespace, in cleanlogmsg, + which we don't try to do here. */ + if (desclen > 0) + { + expand_string (&desc, &descalloc, desclen + 1); + desc[desclen++] = '\012'; + } + + /* Set RCS keyword expansion options. */ + if (options != NULL) + opt = options + 2; + else + opt = NULL; + + if (add_rcs_file (NULL, rcsname, file, NULL, opt, + NULL, NULL, 0, NULL, + desc, desclen, NULL) != 0) + { + if (rcsname != NULL) + free (rcsname); + goto out; + } + rcs = RCS_parsercsfile (rcsname); + newfile = 1; + if (rcsname != NULL) + free (rcsname); + if (desc != NULL) + free (desc); + *rcsnode = rcs; + } + else + { + /* file has existed in the past. Prepare to resurrect. */ + char *rev; + char *oldexpand; + + rcs = *rcsnode; + + oldexpand = RCS_getexpand (rcs); + if ((oldexpand != NULL + && options != NULL + && strcmp (options + 2, oldexpand) != 0) + || (oldexpand == NULL && options != NULL)) + { + /* We tell the user about this, because it means that the + old revisions will no longer retrieve the way that they + used to. */ + error (0, 0, "changing keyword expansion mode to %s", options); + RCS_setexpand (rcs, options + 2); + } + + if (!adding_on_branch) + { + /* We are adding on the trunk, so move the file out of the + Attic. */ + if (!(rcs->flags & INATTIC)) + { + error (0, 0, "warning: expected %s to be in Attic", + rcs->path); + } + + /* Begin a critical section around the code that spans the + first commit on the trunk of a file that's already been + committed on a branch. */ + SIG_beginCrSect (); + + if (RCS_setattic (rcs, 0)) + { + goto out; + } + } + + rev = RCS_getversion (rcs, tag, NULL, 1, (int *) NULL); + /* and lock it */ + if (lock_RCS (file, rcs, rev, repository)) + { + error (0, 0, "cannot lock `%s'.", rcs->path); + if (rev != NULL) + free (rev); + goto out; + } + + if (rev != NULL) + free (rev); + } + + /* when adding a file for the first time, and using a tag, we need + to create a dead revision on the trunk. */ + if (adding_on_branch) + { + if (newfile) + { + char *tmp; + FILE *fp; + int retcode; + + /* 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); + + /* Create empty FILE. Can't use copy_file with a DEVNULL + argument -- copy_file now ignores device files. */ + fp = fopen (file, "w"); + if (fp == NULL) + error (1, errno, "cannot open %s for writing", file); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", file); + + tmp = xmalloc (strlen (file) + strlen (tag) + 80); + /* commit a dead revision. */ + (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); + free (tmp); + if (retcode != 0) + { + error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, + "could not create initial dead revision %s", rcs->path); + goto out; + } + + /* put the new file back where it was */ + rename_file (fname, file); + free (fname); + + /* double-check that the file was written correctly */ + freercsnode (&rcs); + rcs = RCS_parse (file, repository); + if (rcs == NULL) + { + error (0, 0, "could not read %s", rcs->path); + goto out; + } + *rcsnode = rcs; + + /* and lock it once again. */ + if (lock_RCS (file, rcs, NULL, repository)) + { + error (0, 0, "cannot lock `%s'.", rcs->path); + goto out; + } + } + + /* when adding with a tag, we need to stub a branch, if it + doesn't already exist. */ + if (!RCS_nodeisbranch (rcs, tag)) + { + /* branch does not exist. Stub it. */ + char *head; + char *magicrev; + int retcode; + + fixbranch (rcs, sbranch); + + head = RCS_getversion (rcs, NULL, NULL, 0, (int *) NULL); + magicrev = RCS_magicrev (rcs, head); + + retcode = RCS_settag (rcs, tag, magicrev); + RCS_rewrite (rcs, NULL, NULL); + + 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->path); + goto out; + } + } + else + { + /* lock the branch. (stubbed branches need not be locked.) */ + if (lock_RCS (file, rcs, NULL, repository)) + { + error (0, 0, "cannot lock `%s'.", rcs->path); + goto out; + } + } + + if (*rcsnode != rcs) + { + freercsnode(rcsnode); + *rcsnode = rcs; + } + } + + fileattr_newfile (file); + + /* At this point, we used to set the file mode of the RCS file + based on the mode of the file in the working directory. If we + are creating the RCS file for the first time, add_rcs_file does + this already. If we are re-adding the file, then perhaps it is + consistent to preserve the old file mode, just as we preserve + the old keyword expansion mode. + + If we decide that we should change the modes, then we can't do + it here anyhow. At this point, the RCS file may be owned by + somebody else, so a chmod will fail. We need to instead do the + chmod after rewriting it. + + FIXME: In general, I think the file mode (and the keyword + expansion mode) should be associated with a particular revision + of the file, so that it is possible to have different revisions + of a file have different modes. */ + + retval = 0; + + out: + if (retval != 0 && SIG_inCrSect ()) + SIG_endCrSect (); + return retval; +} + + + +/* + * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it + * couldn't. If the RCS file currently has a branch as the head, we must + * move the head back to the trunk before locking the file, and be sure to + * put the branch back as the head if there are any errors. + */ +static int +lock_RCS (const char *user, RCSNode *rcs, const char *rev, + const char *repository) +{ + char *branch = NULL; + int err = 0; + + /* + * For a specified, numeric revision of the form "1" or "1.1", (or when + * no revision is specified ""), definitely move the branch to the trunk + * before locking the RCS file. + * + * The assumption is that if there is more than one revision on the trunk, + * the head points to the trunk, not a branch... and as such, it's not + * necessary to move the head in this case. + */ + if (rev == NULL + || (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2)) + { + branch = xstrdup (rcs->branch); + if (branch != NULL) + { + if (RCS_setbranch (rcs, NULL) != 0) + { + error (0, 0, "cannot change branch to default for %s", + rcs->path); + if (branch) + free (branch); + return 1; + } + } + err = RCS_lock (rcs, NULL, 1); + } + else + { + RCS_lock (rcs, rev, 1); + } + + /* We used to call RCS_rewrite here, and that might seem + appropriate in order to write out the locked revision + information. However, such a call would actually serve no + purpose. CVS locks will prevent any interference from other + CVS processes. The comment above rcs_internal_lockfile + explains that it is already unsafe to use RCS and CVS + simultaneously. It follows that writing out the locked + revision information here would add no additional security. + + If we ever do care about it, the proper fix is to create the + RCS lock file before calling this function, and maintain it + until the checkin is complete. + + The call to RCS_lock is still required at present, since in + some cases RCS_checkin will determine which revision to check + in by looking for a lock. FIXME: This is rather roundabout, + and a more straightforward approach would probably be easier to + understand. */ + + if (err == 0) + { + if (sbranch != NULL) + free (sbranch); + sbranch = branch; + return 0; + } + + /* try to restore the branch if we can on error */ + if (branch != NULL) + fixbranch (rcs, branch); + + if (branch) + free (branch); + return 1; +} + + + +/* + * free an UPDATE node's data + */ +void +update_delproc (Node *p) +{ + struct logfile_info *li = 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); +} + +/* + * Free the commit_info structure in p. + */ +static void +ci_delproc (Node *p) +{ + struct commit_info *ci = p->data; + + if (ci->rev) + free (ci->rev); + if (ci->tag) + free (ci->tag); + if (ci->options) + free (ci->options); + free (ci); +} + +/* + * Free the commit_info structure in p. + */ +static void +masterlist_delproc (Node *p) +{ + struct master_lists *ml = p->data; + + dellist (&ml->ulist); + dellist (&ml->cilist); + free (ml); +} diff --git a/contrib/cvs-1.12.9/src/create_adm.c b/contrib/cvs-1.12.9/src/create_adm.c new file mode 100644 index 0000000000..259f5728c2 --- /dev/null +++ b/contrib/cvs-1.12.9/src/create_adm.c @@ -0,0 +1,162 @@ +/* + * 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 source distribution. + * + * Create Administration. + * + * Creates a CVS administration directory based on the argument repository; the + * "Entries" file is prefilled from the "initrecord" argument. + */ + +#include "cvs.h" + + + +/* update_dir includes dir as its last component. + + Return value is 0 for success, or 1 if we printed a warning message. + Note that many errors are still fatal; particularly for unlikely errors + a fatal error is probably better than a warning which might be missed + or after which CVS might do something non-useful. If WARN is zero, then + don't print warnings; all errors are fatal then. */ + +int +Create_Admin (const char *dir, const char *update_dir, const char *repository, + const char *tag, const char *date, int nonbranch, int warn, + int dotemplate) +{ + FILE *fout; + char *cp; + char *reposcopy; + char *tmp; + + TRACE ( 1, "Create_Admin (%s, %s, %s, %s, %s, %d, %d, %d)", + dir, update_dir, repository, tag ? tag : "", + date ? date : "", nonbranch, warn, dotemplate ); + + if (noexec) + return 0; + + tmp = xmalloc (strlen (dir) + 100); + (void) sprintf (tmp, "%s/%s", dir, CVSADM); + if (isfile (tmp)) + error (1, 0, "there is a version in %s already", update_dir); + + if (CVS_MKDIR (tmp, 0777) < 0) + { + /* We want to print out the entire update_dir, since a lot of + our code calls this function with dir == "." or dir == + NULL. I hope that gives enough information in cases like + absolute pathnames; printing out xgetwd or something would + be way too verbose in the common cases. */ + + if (warn) + { + /* The reason that this is a warning, rather than silently + just skipping creating the directory, is that we don't want + CVS's behavior to vary subtly based on factors (like directory + permissions) which are not made clear to the user. With + the warning at least we let them know what is going on. */ + error (0, errno, "warning: cannot make directory %s in %s", + CVSADM, update_dir); + free (tmp); + return 1; + } + else + error (1, errno, "cannot make directory %s in %s", + CVSADM, update_dir); + } + + /* record the current cvs root for later use */ + + Create_Root (dir, current_parsed_root->original); + if (dir != NULL) + (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP); + else + (void) strcpy (tmp, CVSADM_REP); + fout = CVS_FOPEN (tmp, "w+"); + if (fout == NULL) + { + if (update_dir[0] == '\0') + error (1, errno, "cannot open %s", tmp); + else + error (1, errno, "cannot open %s/%s", update_dir, CVSADM_REP); + } + reposcopy = xstrdup (repository); + Sanitize_Repository_Name (reposcopy); + + /* The top level of the repository is a special case -- we need to + write it with an extra dot at the end. This trailing `.' stuff + rubs me the wrong way -- on the other hand, I don't want to + spend the time making sure all of the code can handle it if we + don't do it. */ + + if (strcmp (reposcopy, current_parsed_root->directory) == 0) + { + reposcopy = xrealloc (reposcopy, strlen (reposcopy) + 3); + strcat (reposcopy, "/."); + } + + cp = reposcopy; + + /* + * If the Repository file is to hold a relative path, try to strip off + * the leading CVSroot argument. + */ + { + char *path = xmalloc (strlen (current_parsed_root->directory) + 2); + + (void) sprintf (path, "%s/", current_parsed_root->directory); + if (strncmp (cp, path, strlen (path)) == 0) + cp += strlen (path); + free (path); + } + + if (fprintf (fout, "%s\n", cp) < 0) + { + if (update_dir[0] == '\0') + error (1, errno, "write to %s failed", tmp); + else + error (1, errno, "write to %s/%s failed", update_dir, CVSADM_REP); + } + if (fclose (fout) == EOF) + { + if (update_dir[0] == '\0') + error (1, errno, "cannot close %s", tmp); + else + error (1, errno, "cannot close %s/%s", update_dir, CVSADM_REP); + } + + /* now, do the Entries file */ + if (dir != NULL) + (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT); + else + (void) strcpy (tmp, CVSADM_ENT); + fout = CVS_FOPEN (tmp, "w+"); + if (fout == NULL) + { + if (update_dir[0] == '\0') + error (1, errno, "cannot open %s", tmp); + else + error (1, errno, "cannot open %s/%s", update_dir, CVSADM_ENT); + } + if (fclose (fout) == EOF) + { + if (update_dir[0] == '\0') + error (1, errno, "cannot close %s", tmp); + else + error (1, errno, "cannot close %s/%s", update_dir, CVSADM_ENT); + } + + /* Create a new CVS/Tag file */ + WriteTag (dir, tag, date, nonbranch, update_dir, repository); + + TRACE (1, "Create_Admin"); + + free (reposcopy); + free (tmp); + return 0; +} diff --git a/contrib/cvs-1.12.9/src/cvs.h b/contrib/cvs-1.12.9/src/cvs.h new file mode 100644 index 0000000000..e422ba0554 --- /dev/null +++ b/contrib/cvs-1.12.9/src/cvs.h @@ -0,0 +1,980 @@ +/* + * 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 + * + */ + + +#ifdef HAVE_CONFIG_H +# include /* this is stuff found via autoconf */ +#endif /* CONFIG_H */ + +/* Add GNU attribute suppport. */ +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# else +# if __GNUC__ == 2 && __GNUC_MINOR__ < 96 +# define __pure__ /* empty */ +# endif +# if __GNUC__ < 3 +# define __malloc__ /* empty */ +# endif +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __const__ const +# define __format__ format +# define __noreturn__ noreturn +# define __printf__ printf +# endif +#endif /* __attribute__ */ + +/* begin GNULIB headers */ +#include "exit.h" +#include "vasnprintf.h" +#include "xalloc.h" +#include "xsize.h" +/* end GNULIB headers */ + +#if ! STDC_HEADERS +char *getenv(); +#endif /* ! STDC_HEADERS */ + +/* Under OS/2, doesn't define popen()/pclose(). */ +#ifdef USE_OWN_POPEN +#include "popen.h" +#endif + +#ifdef SERVER_SUPPORT +/* If the system doesn't provide strerror, it won't be declared in + string.h. */ +char *strerror (int); +#endif + +#include "system.h" + +#include "hash.h" +#include "stack.h" + +#include "root.h" + +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) +# include "client.h" +#endif + +#ifdef MY_NDBM +#include "myndbm.h" +#else +#include +#endif /* MY_NDBM */ + +#include "regex.h" +#include "getopt.h" +#include "wait.h" + +#include "rcs.h" + + + +/* Note that the _ONLY_ reason for PATH_MAX 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. + */ +#include "pathmax.h" + + + +/* 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" +#define CVSADM_ENT "CVS/Entries." +#define CVSADM_ENTBAK "CVS/Entries.Backup" +#define CVSADM_ENTLOG "CVS/Entries.Log" +#define CVSADM_ENTSTAT "CVS/Entries.Static" +#define CVSADM_REP "CVS/Repository." +#define CVSADM_ROOT "CVS/Root." +#define CVSADM_TAG "CVS/Tag." +#define CVSADM_NOTIFY "CVS/Notify." +#define CVSADM_NOTIFYTMP "CVS/Notify.tmp" +#define CVSADM_BASE "CVS/Base" +#define CVSADM_BASEREV "CVS/Baserev." +#define CVSADM_BASEREVTMP "CVS/Baserev.tmp" +#define CVSADM_TEMPLATE "CVS/Template." +#else /* USE_VMS_FILENAMES */ +#define CVSADM "CVS" +#define CVSADM_ENT "CVS/Entries" +#define CVSADM_ENTBAK "CVS/Entries.Backup" +#define CVSADM_ENTLOG "CVS/Entries.Log" +#define CVSADM_ENTSTAT "CVS/Entries.Static" +#define CVSADM_REP "CVS/Repository" +#define CVSADM_ROOT "CVS/Root" +#define CVSADM_TAG "CVS/Tag" +#define CVSADM_NOTIFY "CVS/Notify" +#define CVSADM_NOTIFYTMP "CVS/Notify.tmp" +/* A directory in which we store base versions of files we currently are + editing with "cvs edit". */ +#define CVSADM_BASE "CVS/Base" +#define CVSADM_BASEREV "CVS/Baserev" +#define CVSADM_BASEREVTMP "CVS/Baserev.tmp" +/* File which contains the template for use in log messages. */ +#define CVSADM_TEMPLATE "CVS/Template" +#endif /* USE_VMS_FILENAMES */ + +/* This is the special directory which we use to store various extra + per-directory information in the repository. It must be the same as + CVSADM to avoid creating a new reserved directory name which users cannot + use, but is a separate #define because if anyone changes it (which I don't + recommend), one needs to deal with old, unconverted, repositories. + + See fileattr.h for details about file attributes, the only thing stored + in CVSREP currently. */ +#define CVSREP "CVS" + +/* + * Definitions for the CVSROOT Administrative directory and the files it + * contains. This directory is created as a sub-directory of the $CVSROOT + * environment variable, and holds global administration information for the + * entire source repository beginning at $CVSROOT. + */ +#define CVSROOTADM "CVSROOT" +#define CVSROOTADM_MODULES "modules" +#define CVSROOTADM_LOGINFO "loginfo" +#define CVSROOTADM_RCSINFO "rcsinfo" +#define CVSROOTADM_COMMITINFO "commitinfo" +#define CVSROOTADM_TAGINFO "taginfo" +#define CVSROOTADM_VERIFYMSG "verifymsg" +#define CVSROOTADM_HISTORY "history" +#define CVSROOTADM_VALTAGS "val-tags" +#define CVSROOTADM_IGNORE "cvsignore" +#define CVSROOTADM_CHECKOUTLIST "checkoutlist" +#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 CVSROOTADM_CONFIG "config" + +#define CVSNULLREPOS "Emptydir" /* an empty directory */ + +/* Other CVS file names */ + +/* Files go in the attic if the head main branch revision is dead, + otherwise they go in the regular repository directories. The whole + concept of having an attic is sort of a relic from before death + support but on the other hand, it probably does help the speed of + some operations (such as main branch checkouts and updates). */ +#define CVSATTIC "Attic" + +#define CVSLCK "#cvs.lock" +#define CVSRFL "#cvs.rfl" +#define CVSPFL "#cvs.pfl" +#define CVSWFL "#cvs.wfl" +#define CVSPFLPAT "#cvs.pfl.*" /* wildcard expr to match plocks */ +#define CVSRFLPAT "#cvs.rfl.*" /* wildcard expr to match read locks */ +#define CVSEXT_LOG ",t" +#define CVSPREFIX ",," +#define CVSDOTIGNORE ".cvsignore" +#define CVSDOTWRAPPER ".cvswrappers" + +/* Command attributes -- see function lookup_command_attribute(). */ +#define CVS_CMD_IGNORE_ADMROOT 1 + +/* Set if CVS needs to create a CVS/Root file upon completion of this + command. The name may be slightly confusing, because the flag + isn't really as general purpose as it seems (it is not set for cvs + release). */ + +#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 */ + +#ifdef USE_VMS_FILENAMES +#define BAKPREFIX "_$" +#define DEVNULL "NLA0:" +#else /* USE_VMS_FILENAMES */ +#define BAKPREFIX ".#" /* when rcsmerge'ing */ +#ifndef DEVNULL +#define DEVNULL "/dev/null" +#endif +#endif /* USE_VMS_FILENAMES */ + +/* + * Special tags. -rHEAD refers to the head of an RCS file, regardless of any + * sticky tags. -rBASE refers to the current revision the user has checked + * out This mimics the behaviour of RCS. + */ +#define TAG_HEAD "HEAD" +#define TAG_BASE "BASE" + +/* Environment variable used by CVS */ +#define CVSREAD_ENV "CVSREAD" /* make files read-only */ +#define CVSREAD_DFLT 0 /* writable files by default */ + +#define CVSREADONLYFS_ENV "CVSREADONLYFS" /* repository is read-only */ + +#define TMPDIR_ENV "TMPDIR" /* Temporary directory */ +#define CVS_PID_ENV "CVS_PID" /* pid of running cvs */ + +#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 CVSROOT_ENV "CVSROOT" /* source directory root */ +#define CVSROOT_DFLT NULL /* No dflt; must set for checkout */ + +#define IGNORE_ENV "CVSIGNORE" /* More files to ignore */ +#define WRAPPER_ENV "CVSWRAPPERS" /* name of the wrapper file */ + +#define CVSUMASK_ENV "CVSUMASK" /* Effective umask for repository */ + +/* + * If the beginning of the Repository matches the following string, strip it + * so that the output to the logfile does not contain a full pathname. + * + * If the CVSROOT environment variable is set, it overrides this define. + */ +#define REPOS_STRIP "/master/" + +/* 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; + + /* Timestamp, or "" if none (never NULL). */ + char *timestamp; + + /* Keyword expansion options, or "" if none (never NULL). */ + char *options; + + char *tag; + char *date; + char *conflict; +}; +typedef struct entnode Entnode; + +/* The type of request that is being done in do_module() */ +enum mtype +{ + CHECKOUT, TAG, PATCH, EXPORT, MISC +}; + +/* + * structure used for list-private storage by Entries_Open() and + * 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; + 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 */ +#define W_LOCAL 0x01 /* look for files locally */ +#define W_REPOS 0x02 /* look for files in the repository */ +#define W_ATTIC 0x04 /* look for files in the attic */ + +/* Flags for return values of direnter procs for the recursion processor */ +enum direnter_type +{ + R_PROCESS = 1, /* process files and maybe dirs */ + R_SKIP_FILES, /* don't process files in this dir */ + R_SKIP_DIRS, /* don't process sub-dirs */ + R_SKIP_ALL /* don't process files or dirs */ +}; +#ifdef ENUMS_CAN_BE_TROUBLE +typedef int Dtype; +#else +typedef enum direnter_type Dtype; +#endif + +/* Recursion processor lock types */ +#define CVS_LOCK_NONE 0 +#define CVS_LOCK_READ 1 +#define CVS_LOCK_WRITE 2 + +/* Option flags for Parse_Info() */ +#define PIOPT_ALL 1 /* accept "all" keyword */ + +extern const char *program_name, *program_path, *cvs_cmd_name; +extern char *Tmpdir, *Editor; +extern int cvsadmin_root; +extern char *CurDir; +extern int really_quiet, quiet; +extern int use_editor; +extern int cvswrite; +extern mode_t cvsumask; + + + +/* This global variable holds the global -d option. It is NULL if -d + was not used, which means that we must get the CVSroot information + from the CVSROOT environment variable or from a CVS/Root file. */ +extern char *CVSroot_cmdline; + +/* These variables keep track of all of the CVSROOT directories that + have been seen by the client and the current one of those selected. */ +extern List *root_directories; +extern cvsroot_t *current_parsed_root; + +char *emptydir_name (void); +int safe_location (char *); + +extern int trace; /* Show all commands */ +extern int noexec; /* Don't modify disk anywhere */ +extern int readonlyfs; /* fail on all write locks; succeed all read locks */ +extern int logoff; /* Don't write history entry */ + +extern int top_level_admin; +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS +extern int UseNewInfoFmtStrings; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + + +#define LOGMSG_REREAD_NEVER 0 /* do_verify - never reread message */ +#define LOGMSG_REREAD_ALWAYS 1 /* do_verify - always reread message */ +#define LOGMSG_REREAD_STAT 2 /* do_verify - reread message if changed */ +extern int RereadLogAfterVerify; + +#ifdef CLIENT_SUPPORT +extern List *dirs_sent_to_server; /* used to decide which "Argument + xxx" commands to send to each + server in multiroot mode. */ +#endif + +extern char hostname[]; + +/* Externs that are included directly in the CVS sources */ + +int RCS_merge (RCSNode *, const char *, const char *, const char *, + const char *, const char *); +/* Flags used by RCS_* functions. See the description of the individual + functions for which flags mean what for each function. */ +#define RCS_FLAGS_FORCE 1 +#define RCS_FLAGS_DEAD 2 +#define RCS_FLAGS_QUIET 4 +#define RCS_FLAGS_MODTIME 8 +#define RCS_FLAGS_KEEPFILE 16 + +int RCS_exec_rcsdiff (RCSNode *rcsfile, + const char *opts, const char *options, + const char *rev1, const char *rev1_cache, + const char *rev2, + const char *label1, const char *label2, + const char *workfile); +int diff_exec (const char *file1, const char *file2, + const char *label1, const char *label2, + const char *options, const char *out); + + +#include "error.h" + +DBM *open_module (void); +FILE *open_file (const char *, const char *); +List *Find_Directories (char *repository, int which, List *entries); +void Entries_Close (List *entries); +List *Entries_Open (int aflag, char *update_dir); +void Subdirs_Known (List *entries); +void Subdir_Register (List *, const char *, const char *); +void Subdir_Deregister (List *, const char *, const char *); + +char *Make_Date (char *rawdate); +char *date_from_time_t (time_t); +void date_to_internet (char *, const char *); +void date_to_tm (struct tm *, const char *); +void tm_to_internet (char *, const struct tm *); +char *gmformat_time_t (time_t unixtime); +char *format_date_alloc (char *text); + +char *Name_Repository (const char *dir, const char *update_dir); +const char *Short_Repository (const char *repository); +void Sanitize_Repository_Name (char *repository); + +char *Name_Root (char *dir, char *update_dir); +void free_cvsroot_t (cvsroot_t *root_in); +cvsroot_t *parse_cvsroot (const char *root) + __attribute__ ((__malloc__)); +cvsroot_t *local_cvsroot (const char *dir) + __attribute__ ((__malloc__)); +void Create_Root (const char *dir, const char *rootdir); +void root_allow_add (char *); +void root_allow_free (void); +int root_allow_ok (char *); + +char *previous_rev (RCSNode *rcs, const char *rev); +char *gca (const char *rev1, const char *rev2); +void check_numeric (const char *, int, char **); +char *getcaller (void); +char *entries_time (time_t unixtime); +time_t unix_time_stamp (const char *file); +char *time_stamp (const char *file); + +void *xmalloc (size_t bytes) + __attribute__((__malloc__)); +void *xrealloc (void *ptr, size_t bytes) + __attribute__ ((__malloc__)); +void expand_string (char **, size_t *, size_t); +void xrealloc_and_strcat (char **, size_t *, const char *); +char *xstrdup (const char *str) + __attribute__ ((__malloc__)); +int strip_trailing_newlines (char *str); +int pathname_levels (const char *path); + +typedef int (*CALLPROC) (const char *repository, const char *value, + void *closure); +int Parse_Info (const char *infofile, const char *repository, + CALLPROC callproc, int opt, void *closure); +int parse_config (char *); + +typedef RETSIGTYPE (*SIGCLEANUPPROC) (); +int SIG_register (int sig, SIGCLEANUPPROC sigcleanup); +int isdir (const char *file); +int isfile (const char *file); +int islink (const char *file); +int isdevice (const char *file); +int isreadable (const char *file); +int iswritable (const char *file); +int isaccessible (const char *file, const int mode); +int isabsolute (const char *filename); +#ifdef HAVE_READLINK +char *xreadlink (const char *link); +#endif /* HAVE_READLINK */ +char *xresolvepath (const char *path); +const char *last_component (const char *path); +char *get_homedir (void); +char *strcat_filename_onto_homedir (const char *, const char *); +char *cvs_temp_name (void); +FILE *cvs_temp_file (char **filename); + +int numdots (const char *s); +char *increment_revnum (const char *); +int compare_revnums (const char *, const char *); +int ls (int argc, char *argv[]); +int unlink_file (const char *f); +int unlink_file_dir (const char *f); + +/* This is the structure that the recursion processor passes to the + fileproc to tell it about a particular file. */ +struct file_info +{ + /* Name of the file, without any directory component. */ + const char *file; + + /* Name of the directory we are in, relative to the directory in + which this command was issued. We have cd'd to this directory + (either in the working directory or in the repository, depending + on which sort of recursion we are doing). If we are in the directory + in which the command was issued, this is "". */ + const char *update_dir; + + /* update_dir and file put together, with a slash between them as + necessary. This is the proper way to refer to the file in user + messages. */ + const char *fullname; + + /* Name of the directory corresponding to the repository which contains + this file. */ + const char *repository; + + /* The pre-parsed entries for this directory. */ + List *entries; + + RCSNode *rcs; +}; + +int update (int argc, char *argv[]); +/* The only place this is currently used outside of update.c is add.c. + * Restricting its use to update.c seems to be in the best interest of + * modularity, but I can't think of a good way to get an update of a + * resurrected file done and print the fact otherwise. + */ +void write_letter (struct file_info *finfo, int letter); +int xcmp (const char *file1, const char *file2); +int yesno (void); +void *valloc (size_t bytes); + +/* Need this until we back out the get_date () proto again and use a current + * version of getdate.y from GNULIB. + */ +#include "xtime.h" +time_t get_date (char *date, struct timeb *now); + +int Create_Admin (const char *dir, const char *update_dir, + const char *repository, const char *tag, const char *date, + int nonbranch, int warn, int dotemplate); +int expand_at_signs (const char *, off_t, FILE *); + +/* Locking subsystem (implemented in lock.c). */ + +int Reader_Lock (char *xrepository); +void Simple_Lock_Cleanup (void); +void Lock_Cleanup (void); + +/* Writelock an entire subtree, well the part specified by ARGC, ARGV, LOCAL, + and AFLAG, anyway. */ +void lock_tree_promotably (int argc, char **argv, int local, int which, + int aflag); + +/* See lock.c for description. */ +void lock_dir_for_write (const char *); + +/* LockDir setting from CVSROOT/config. */ +extern char *lock_dir; + +/* AllowedAdminOptions setting from CVSROOT/config. */ +extern char *UserAdminOptions; + +void Scratch_Entry (List * list, const char *fname); +void ParseTag (char **tagp, char **datep, int *nonbranchp); +void WriteTag (const char *dir, const char *tag, const char *date, + int nonbranch, const char *update_dir, const char *repository); +void WriteTemplate (const char *update_dir, int dotemplate, + const char *repository); +void cat_module (int status); +void check_entries (char *dir); +void close_module (DBM * db); +void copy_file (const char *from, const char *to); +void fperrmsg (FILE * fp, int status, int errnum, char *message,...); +void free_names (int *pargc, char *argv[]); + +int ign_name (char *name); +void ign_add (char *ign, int hold); +void ign_add_file (char *file, int hold); +void ign_setup (void); +void ign_dir_add (char *name); +int ignore_directory (const char *name); +typedef void (*Ignore_proc) (const char *, const char *); +void ignore_files (List *, List *, const char *, Ignore_proc); +extern int ign_inhibit_server; + +#include "update.h" + +void line2argv (int *pargc, char ***argv, char *line, char *sepchars); +void make_directories (const char *name); +void make_directory (const char *name); +int mkdir_if_needed (const char *name); +void rename_file (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 + conventions regarding wildcard syntax. It might be desirable to change the + former in the future (e.g. "cvs status *.h" including files which don't exist + in the working directory). The result is placed in *PARGC and *PARGV; + the *PARGV array itself and all the strings it contains are newly + malloc'd. It is OK to call it with PARGC == &ARGC or PARGV == &ARGV. */ +void expand_wild (int argc, char **argv, + int *pargc, char ***pargv); + +#ifdef SERVER_SUPPORT +int cvs_casecmp (const char *, const char *); +#endif + +/* exithandle.c */ +void signals_register (RETSIGTYPE (*handler)(int)); +void cleanup_register (void (*handler) (void)); + +void strip_trailing_slashes (char *path); +void update_delproc (Node * p); +void usage (const char *const *cpp); +void xchmod (const char *fname, int writable); +char *xgetwd (void); +List *Find_Names (char *repository, int which, int aflag, + List ** optentries); +void Register (List * list, const char *fname, const char *vn, const char *ts, + const char *options, const char *tag, const char *date, + const char *ts_conflict); +void Update_Logfile (const char *repository, const char *xmessage, + FILE * xlogfp, List * xchanges); +void do_editor (const char *dir, char **messagep, + const char *repository, List * changes); + +void do_verify (char **messagep, const char *repository); + +typedef int (*CALLBACKPROC) (int argc, char *argv[], char *where, + char *mwhere, char *mfile, int shorten, int local_specified, + char *omodule, char *msg); + + +typedef int (*FILEPROC) (void *callerdat, struct file_info *finfo); +typedef int (*FILESDONEPROC) (void *callerdat, int err, + const char *repository, const char *update_dir, + List *entries); +typedef Dtype (*DIRENTPROC) (void *callerdat, const char *dir, + const char *repos, const char *update_dir, + List *entries); +typedef int (*DIRLEAVEPROC) (void *callerdat, const char *dir, int err, + const char *update_dir, List *entries); + +int mkmodules (char *dir); +int init (int argc, char **argv); + +int do_module (DBM * db, char *mname, enum mtype m_type, char *msg, + CALLBACKPROC callback_proc, char *where, int shorten, + int local_specified, int run_module_prog, int build_dirs, + char *extra_arg); +void history_write (int type, const char *update_dir, const char *revs, + const char *name, const char *repository); +int start_recursion (FILEPROC fileproc, FILESDONEPROC filesdoneproc, + DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc, + void *callerdat, + int argc, char *argv[], int local, int which, + int aflag, int locktype, char *update_preload, + int dosrcs, char *repository); +void SIG_beginCrSect (void); +void SIG_endCrSect (void); +int SIG_inCrSect (void); +void read_cvsrc (int *argc, char ***argv, const char *cmdname); + +char *make_message_rcsvalid (const char *message); +int file_has_conflict (const struct file_info *, + const char *ts_conflict); +int file_has_markers (const struct file_info *); +void get_file (const char *, const char *, const char *, + char **, size_t *, size_t *); +char *shell_escape (char *buf, const char *str); +char *backup_file (const char *file, const char *suffix); +void resolve_symlink (char **filename); +void sleep_past (time_t desttime); + +/* flags for run_exec(), the fast system() for CVS */ +#define RUN_NORMAL 0x0000 /* no special behaviour */ +#define RUN_COMBINED 0x0001 /* stdout is duped to stderr */ +#define RUN_REALLY 0x0002 /* do the exec, even if noexec is on */ +#define RUN_STDOUT_APPEND 0x0004 /* append to stdout, don't truncate */ +#define RUN_STDERR_APPEND 0x0008 /* append to stderr, don't truncate */ +#define RUN_SIGIGNORE 0x0010 /* ignore interrupts for command */ +#define RUN_TTY (char *)0 /* for the benefit of lint */ + +void run_arg (const char *s); +void run_print (FILE * fp); +void run_setup (const char *prog); +int run_exec (const char *stin, const char *stout, const char *sterr, + int flags); +/* for format_cmdline function - when a list variable is bound to a user string, + * we need to pass some data through walklist into the callback function. + * We use this struct. + */ +struct format_cmdline_walklist_closure +{ + const char *format; /* the format string the user passed us */ + char **buf; /* *dest = our NUL terminated and possibly too short + * destination string + */ + size_t *length; /* *dlen = how many bytes have already been allocated to + * *dest. + */ + char **d; /* our pointer into buf where the next char should go */ + char quotes; /* quotes we are currently between, if any */ +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + int onearg; + int firstpass; + const char *srepos; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + void *closure; /* our user defined closure */ +}; +char *cmdlinequote (char quotes, char *s); +char *cmdlineescape (char quotes, char *s); +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS +char *format_cmdline (int oldway, const char *srepos, const char *format, ...); +#else /* SUPPORT_OLD_INFO_FMT_STRINGS */ +char *format_cmdline (const char *format, ...); +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + +/* other similar-minded stuff from run.c. */ +FILE *run_popen (const char *, const char *); +int piped_child (char *const *, int *, int *); +void close_on_exec (int); + +pid_t waitpid (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: + + NULL = file is not mentioned in Entries (this is also used for a + directory). + "" = INVALID! The comment used to say that it meant "no user file" + but as far as I know CVS didn't actually use it that way. + Note that according to cvs.texinfo, "" is not valid in the + Entries 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), malloc'd. If none, + then it is an empty string (never NULL). */ + 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 (struct file_info *finfo, char *options, char *tag, + char *date, int force_tag_match, + int set_time); +void freevers_ts (Vers_TS ** versp); + +/* Miscellaneous CVS infrastructure which layers on top of the recursion + processor (for example, needs struct file_info). */ + +int Checkin (int type, struct file_info *finfo, char *rev, + char *tag, char *options, char *message); +int No_Difference (struct file_info *finfo, Vers_TS *vers); +/* TODO: can the finfo argument to special_file_mismatch be changed? -twp */ +int special_file_mismatch (struct file_info *finfo, + char *rev1, char *rev2); + +/* CVSADM_BASEREV stuff, from entries.c. */ +char *base_get (struct file_info *); +void base_register (struct file_info *, char *); +void base_deregister (struct file_info *); + +/* + * defines for Classify_File() to determine the current state of a file. + * These are also used as types in the data field for the list we make for + * Update_Logfile in commit, import, and add. + */ +enum classify_type +{ + T_UNKNOWN = 1, /* no old-style analog existed */ + T_CONFLICT, /* C (conflict) list */ + T_NEEDS_MERGE, /* G (needs merging) list */ + T_MODIFIED, /* M (needs checked in) list */ + T_CHECKOUT, /* O (needs checkout) list */ + T_ADDED, /* A (added file) list */ + T_REMOVED, /* R (removed file) list */ + T_REMOVE_ENTRY, /* W (removed entry) list */ + T_UPTODATE, /* File is up-to-date */ + T_PATCH, /* P Like C, but can patch */ + T_TITLE /* title for node type */ +}; +typedef enum classify_type Ctype; + +Ctype Classify_File (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 { + /* -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 (void); +int wrap_name_has (const char *name,WrapMergeHas has); +char *wrap_rcsoption (const char *fileName, int asFlag); +char *wrap_tocvs_process_file (const char *fileName); +int wrap_merge_is_copy (const char *fileName); +void wrap_fromcvs_process_file (const char *fileName); +void wrap_add_file (const char *file,int temp); +void wrap_add (char *line,int temp); +void wrap_send (void); +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) +void wrap_unparse_rcs_options (char **, int); +#endif /* SERVER_SUPPORT || CLIENT_SUPPORT */ + +/* Pathname expansion */ +char *expand_path (const char *name, const char *file, int line, + int formatsafe); + +/* User variables. */ +extern List *variable_list; + +void variable_set (char *nameval); + +int watch (int argc, char **argv); +int edit (int argc, char **argv); +int unedit (int argc, char **argv); +int editors (int argc, char **argv); +int watchers (int argc, char **argv); +int annotate (int argc, char **argv); +int add (int argc, char **argv); +int admin (int argc, char **argv); +int checkout (int argc, char **argv); +int commit (int argc, char **argv); +int diff (int argc, char **argv); +int history (int argc, char **argv); +int import (int argc, char **argv); +int cvslog (int argc, char **argv); +#ifdef AUTH_CLIENT_SUPPORT +/* Some systems (namely Mac OS X) have conflicting definitions for these + * functions. Avoid them. + */ +#ifdef HAVE_LOGIN +# define login cvs_login +#endif /* HAVE_LOGIN */ +#ifdef HAVE_LOGOUT +# define logout cvs_logout +#endif /* HAVE_LOGOUT */ +int login (int argc, char **argv); +int logout (int argc, char **argv); +#endif /* AUTH_CLIENT_SUPPORT */ +int patch (int argc, char **argv); +int release (int argc, char **argv); +int cvsremove (int argc, char **argv); +int rtag (int argc, char **argv); +int cvsstatus (int argc, char **argv); +int cvstag (int argc, char **argv); +int version (int argc, char **argv); + +unsigned long int lookup_command_attribute (char *); + +#if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT) +char *scramble (char *str); +char *descramble (char *str); +#endif /* AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT */ + +#ifdef AUTH_CLIENT_SUPPORT +char *get_cvs_password (void); +/* get_cvs_port_number() is not pure since the /etc/services file could change + * between calls. */ +int get_cvs_port_number (const cvsroot_t *root); +/* normalize_cvsroot() is not pure since it calls get_cvs_port_number. */ +char *normalize_cvsroot (const cvsroot_t *root) + __attribute__ ((__malloc__)); +#endif /* AUTH_CLIENT_SUPPORT */ + +void tag_check_valid (char *, int, char **, int, int, char *); +void tag_check_valid_join (char *, int, char **, int, int, + char *); + +#include "server.h" + +/* From server.c and documented there. */ +void cvs_output (const char *, size_t); +void cvs_output_binary (char *, size_t); +void cvs_outerr (const char *, size_t); +void cvs_flusherr (void); +void cvs_flushout (void); +void cvs_output_tagged (const char *, const char *); + +/* The trace function from subr.c */ +void cvs_trace (int level, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +#define TRACE cvs_trace +/* Trace levels: + * + * TRACE_FUNCTION Trace function calls, often including function + * arguments. This is the trace level that, historically, + * applied to all trace calls. + * TRACE_FLOW Include the flow control functions, such as + * start_recursion, do_recursion, and walklist in the + * function traces. + * TRACE_DATA Trace important internal function data. + */ +#define TRACE_FUNCTION 1 +#define TRACE_FLOW 2 +#define TRACE_DATA 3 diff --git a/contrib/cvs-1.12.9/src/cvsbug.in b/contrib/cvs-1.12.9/src/cvsbug.in new file mode 100644 index 0000000000..efc156d35f --- /dev/null +++ b/contrib/cvs-1.12.9/src/cvsbug.in @@ -0,0 +1,527 @@ +#! /bin/sh +# Submit a problem report to a GNATS site. +# Copyright (C) 1993 Free Software Foundation, Inc. +# Contributed by Brendan Kehoe (brendan@cygnus.com), based on a +# version written by Heinz G. Seidl (hgs@ide.com). +# +# This file is part of GNU GNATS. +# Modified by Berliner for CVS. +# +# 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) +# any later version. +# +# GNU GNATS 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 version of this send-pr. +VERSION=3.2 + +# The submitter-id for your site. +SUBMITTER=net + +## # Where the GNATS directory lives, if at all. +## [ -z "$GNATS_ROOT" ] && +## GNATS_ROOT=/usr/local/lib/gnats/gnats-db + +# The default mail address for PR submissions. +GNATS_ADDR=@PACKAGE_BUGREPORT@ + +## # Where the gnats category tree lives. +## DATADIR=/usr/local/lib + +## # If we've been moved around, try using GCC_EXEC_PREFIX. +## [ ! -d $DATADIR/gnats -a -d "$GCC_EXEC_PREFIX" ] && DATADIR=${GCC_EXEC_PREFIX}.. + +# The default release for this host. +DEFAULT_RELEASE="@VERSION@" + +# The default organization. +DEFAULT_ORGANIZATION="net" + +## # The default site to look for. +## GNATS_SITE=unknown + +## # Newer config information? +## [ -f ${GNATS_ROOT}/gnats-adm/config ] && . ${GNATS_ROOT}/gnats-adm/config + +# Hack mktemp on systems that don't have it. +@MKTEMP_SH_FUNCTION@ +MKTEMP="@MKTEMP@" + +# What mailer to use. This must come after the config file, since it is +# host-dependent. +SENDMAIL="@SENDMAIL@" +MAIL_AGENT="$SENDMAIL -oi -t" +MAILER=`echo $MAIL_AGENT | sed -e 's, .*,,'` +if [ ! -f "$MAILER" ] ; then + echo "$COMMAND: Cannot find mail program \"$MAILER\"." + echo "$COMMAND: Please fix the MAIL_AGENT entry in the $COMMAND file." + exit 1 +fi + +if test "`echo -n foo`" = foo ; then + ECHON=bsd +elif test "`echo 'foo\c'`" = foo ; then + ECHON=sysv +else + ECHON=none +fi + +if [ $ECHON = bsd ] ; then + ECHON1="echo -n" + ECHON2= +elif [ $ECHON = sysv ] ; then + ECHON1=echo + ECHON2='\c' +else + ECHON1=echo + ECHON2= +fi + +# + +[ -z "$TMPDIR" ] && TMPDIR=/tmp + +TEMP="`$MKTEMP $TMPDIR/p.XXXXXX`" +BAD="`$MKTEMP $TMPDIR/pbad.XXXXXX`" +REF="`$MKTEMP $TMPDIR/pf.XXXXXX`" + +if [ -z "$LOGNAME" -a -n "$USER" ]; then + LOGNAME=$USER +fi + +FROM="$LOGNAME" +REPLY_TO="$LOGNAME" + +# Find out the name of the originator of this PR. +if [ -n "$NAME" ]; then + ORIGINATOR="$NAME" +elif [ -f $HOME/.fullname ]; then + ORIGINATOR="`sed -e '1q' $HOME/.fullname`" +elif [ -f /bin/domainname ]; then + if [ "`/bin/domainname`" != "" -a -f /usr/bin/ypcat ]; then + # Must use temp file due to incompatibilities in quoting behavior + # and to protect shell metacharacters in the expansion of $LOGNAME + /usr/bin/ypcat passwd 2>/dev/null | cat - /etc/passwd | grep "^$LOGNAME:" | + cut -f5 -d':' | sed -e 's/,.*//' > $TEMP + ORIGINATOR="`cat $TEMP`" + rm -f $TEMP + fi +fi + +if [ "$ORIGINATOR" = "" ]; then + grep "^$LOGNAME:" /etc/passwd | cut -f5 -d':' | sed -e 's/,.*//' > $TEMP + ORIGINATOR="`cat $TEMP`" + rm -f $TEMP +fi + +if [ -n "$ORGANIZATION" ]; then + if [ -f "$ORGANIZATION" ]; then + ORGANIZATION="`cat $ORGANIZATION`" + fi +else + if [ -n "$DEFAULT_ORGANIZATION" ]; then + ORGANIZATION="$DEFAULT_ORGANIZATION" + elif [ -f $HOME/.organization ]; then + ORGANIZATION="`cat $HOME/.organization`" + elif [ -f $HOME/.signature ]; then + ORGANIZATION="`cat $HOME/.signature`" + fi +fi + +# If they don't have a preferred editor set, then use +if [ -z "$VISUAL" ]; then + if [ -z "$EDITOR" ]; then + EDIT=vi + else + EDIT="$EDITOR" + fi +else + EDIT="$VISUAL" +fi + +# Find out some information. +SYSTEM=`( [ -f /bin/uname ] && /bin/uname -a ) || \ + ( [ -f /usr/bin/uname ] && /usr/bin/uname -a ) || echo ""` +ARCH=`[ -f /bin/arch ] && /bin/arch` +MACHINE=`[ -f /bin/machine ] && /bin/machine` + +COMMAND=`echo $0 | sed -e 's,.*/,,'` +## USAGE="Usage: $COMMAND [-PVL] [-t address] [-f filename] [--request-id] +USAGE="Usage: $COMMAND [-PVL] +[--version]" +REMOVE= +BATCH= + +while [ $# -gt 0 ]; do + case "$1" in + -r) ;; # Ignore for backward compat. +## -t | --to) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi +## shift ; GNATS_ADDR="$1" +## EXPLICIT_GNATS_ADDR=true +## ;; +## -f | --file) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi +## shift ; IN_FILE="$1" +## if [ "$IN_FILE" != "-" -a ! -r "$IN_FILE" ]; then +## echo "$COMMAND: cannot read $IN_FILE" +## exit 1 +## fi +## ;; + -b | --batch) BATCH=true ;; + -p | -P | --print) PRINT=true ;; + -L | --list) FORMAT=norm ;; + -l | -CL | --lisp) FORMAT=lisp ;; +## --request-id) REQUEST_ID=true ;; + -h | --help) echo "$USAGE"; exit 0 ;; + -V | --version) echo "$VERSION"; exit 0 ;; + -*) echo "$USAGE" ; exit 1 ;; + *) echo "$USAGE" ; exit 1 +## if [ -z "$USER_GNATS_SITE" ]; then +## if [ ! -r "$DATADIR/gnats/$1" ]; then +## echo "$COMMAND: the GNATS site $1 does not have a categories list." +## exit 1 +## else +## # The site name is the alias they'll have to have created. +## USER_GNATS_SITE=$1 +## fi +## else +## echo "$USAGE" ; exit 1 +## fi + ;; + esac + shift +done + +if [ -n "$USER_GNATS_SITE" ]; then + GNATS_SITE=$USER_GNATS_SITE + GNATS_ADDR=$USER_GNATS_SITE-gnats +fi + +if [ "$SUBMITTER" = "unknown" -a -z "$REQUEST_ID" -a -z "$IN_FILE" ]; then + cat << '__EOF__' +It seems that send-pr is not installed with your unique submitter-id. +You need to run + + install-sid YOUR-SID + +where YOUR-SID is the identification code you received with `send-pr'. +`send-pr' will automatically insert this value into the template field +`>Submitter-Id'. If you've downloaded `send-pr' from the Net, use `net' +for this value. If you do not know your id, run `send-pr --request-id' to +get one from your support site. +__EOF__ + exit 1 +fi + +## if [ -r "$DATADIR/gnats/$GNATS_SITE" ]; then +## CATEGORIES=`grep -v '^#' $DATADIR/gnats/$GNATS_SITE | sort` +## else +## echo "$COMMAND: could not read $DATADIR/gnats/$GNATS_SITE for categories list." +## exit 1 +## fi +CATEGORIES="contrib cvs doc pcl-cvs portability" + +if [ -z "$CATEGORIES" ]; then + echo "$COMMAND: the categories list for $GNATS_SITE was empty!" + exit 1 +fi + +case "$FORMAT" in + lisp) echo "$CATEGORIES" | \ + 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); } } + END {print max + 1;}'` + c=`expr 70 / $l` + if [ $c -eq 0 ]; then c=1; fi + echo "$CATEGORIES" | \ + awk 'BEGIN {print "Known categories:"; i = 0 } + { printf ("%-'$l'.'$l's", $0); if ((++i % '$c') == 0) { print "" } } + END { print ""; }' + exit 0 + ;; +esac + +ORIGINATOR_C='' +ORGANIZATION_C='' +CONFIDENTIAL_C='<[ yes | no ] (one line)>' +SYNOPSIS_C='' +SEVERITY_C='<[ non-critical | serious | critical ] (one line)>' +PRIORITY_C='<[ low | medium | high ] (one line)>' +CATEGORY_C='' +CLASS_C='<[ sw-bug | doc-bug | change-request | support ] (one line)>' +RELEASE_C='' +ENVIRONMENT_C='' +DESCRIPTION_C='' +HOW_TO_REPEAT_C='' +FIX_C='' + +# Catch some signals. ($xs kludge needed by Sun /bin/sh) +xs=0 +trap 'rm -f $REF $TEMP; exit $xs' 0 +trap 'echo "$COMMAND: Aborting ..."; rm -f $REF $TEMP; xs=1; exit' 1 2 3 13 15 + +# If they told us to use a specific file, then do so. +if [ -n "$IN_FILE" ]; then + if [ "$IN_FILE" = "-" ]; then + # The PR is coming from the standard input. + if [ -n "$EXPLICIT_GNATS_ADDR" ]; then + sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" > $TEMP + else + cat > $TEMP + fi + else + # Use the file they named. + if [ -n "$EXPLICIT_GNATS_ADDR" ]; then + sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" $IN_FILE > $TEMP + else + cat $IN_FILE > $TEMP + fi + fi +else + + if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then + # If their PR_FORM points to a bogus entry, then bail. + if [ ! -f "$PR_FORM" -o ! -r "$PR_FORM" -o ! -s "$PR_FORM" ]; then + echo "$COMMAND: can't seem to read your template file (\`$PR_FORM'), ignoring PR_FORM" + sleep 1 + PRINT_INTERN=bad_prform + fi + fi + + if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then + cp $PR_FORM $TEMP || + ( echo "$COMMAND: could not copy $PR_FORM" ; xs=1; exit ) + else + for file in $TEMP $REF ; do + cat > $file << '__EOF__' +SEND-PR: -*- send-pr -*- +SEND-PR: Lines starting with `SEND-PR' will be removed automatically, as +SEND-PR: will all comments (text enclosed in `<' and `>'). +SEND-PR: +SEND-PR: Choose from the following categories: +SEND-PR: +__EOF__ + + # Format the categories so they fit onto lines. + l=`echo "$CATEGORIES" | \ + 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 + echo "$CATEGORIES" | \ + awk 'BEGIN {printf "SEND-PR: "; i = 0 } + { printf ("%-'$l'.'$l's", $0); + if ((++i % '$c') == 0) { printf "\nSEND-PR: " } } + END { printf "\nSEND-PR:\n"; }' >> $file + + cat >> $file << __EOF__ +To: $GNATS_ADDR +Subject: +From: $FROM +Reply-To: $REPLY_TO +X-send-pr-version: $VERSION + + +>Submitter-Id: $SUBMITTER +>Originator: $ORIGINATOR +>Organization: +${ORGANIZATION-$ORGANIZATION_C} +>Confidential: $CONFIDENTIAL_C +>Synopsis: $SYNOPSIS_C +>Severity: $SEVERITY_C +>Priority: $PRIORITY_C +>Category: $CATEGORY_C +>Class: $CLASS_C +>Release: ${DEFAULT_RELEASE-$RELEASE_C} +>Environment: + $ENVIRONMENT_C +`[ -n "$SYSTEM" ] && echo System: $SYSTEM` +`[ -n "$ARCH" ] && echo Architecture: $ARCH` +`[ -n "$MACHINE" ] && echo Machine: $MACHINE` +>Description: + $DESCRIPTION_C +>How-To-Repeat: + $HOW_TO_REPEAT_C +>Fix: + $FIX_C +__EOF__ + done + fi + + if [ "$PRINT" = true -o "$PRINT_INTERN" = true ]; then + cat $TEMP + xs=0; exit + fi + + chmod u+w $TEMP + if [ -z "$REQUEST_ID" ]; then + eval $EDIT $TEMP + else + ed -s $TEMP << '__EOF__' +/^Subject/s/^Subject:.*/Subject: request for a customer id/ +/^>Category/s/^>Category:.*/>Category: send-pr/ +w +q +__EOF__ + fi + + if cmp -s $REF $TEMP ; then + echo "$COMMAND: problem report not filled out, therefore not sent" + xs=1; exit + fi +fi + +# +# Check the enumeration fields + +# This is a "sed-subroutine" with one keyword parameter +# (with workaround for Sun sed bug) +# +SED_CMD=' +/$PATTERN/{ +s||| +s|<.*>|| +s|^[ ]*|| +s|[ ]*$|| +p +q +}' + + +while [ -z "$REQUEST_ID" ]; do + CNT=0 + + # 1) Confidential + # + PATTERN=">Confidential:" + CONFIDENTIAL=`eval sed -n -e "\"$SED_CMD\"" $TEMP` + case "$CONFIDENTIAL" in + ""|yes|no) CNT=`expr $CNT + 1` ;; + *) echo "$COMMAND: \`$CONFIDENTIAL' is not a valid value for \`Confidential'." ;; + esac + # + # 2) Severity + # + PATTERN=">Severity:" + SEVERITY=`eval sed -n -e "\"$SED_CMD\"" $TEMP` + case "$SEVERITY" in + ""|non-critical|serious|critical) CNT=`expr $CNT + 1` ;; + *) echo "$COMMAND: \`$SEVERITY' is not a valid value for \`Severity'." + esac + # + # 3) Priority + # + PATTERN=">Priority:" + PRIORITY=`eval sed -n -e "\"$SED_CMD\"" $TEMP` + case "$PRIORITY" in + ""|low|medium|high) CNT=`expr $CNT + 1` ;; + *) echo "$COMMAND: \`$PRIORITY' is not a valid value for \`Priority'." + esac + # + # 4) Category + # + PATTERN=">Category:" + CATEGORY=`eval sed -n -e "\"$SED_CMD\"" $TEMP` + FOUND= + for C in $CATEGORIES + do + if [ "$C" = "$CATEGORY" ]; then FOUND=true ; break ; fi + done + if [ -n "$FOUND" ]; then + CNT=`expr $CNT + 1` + else + if [ -z "$CATEGORY" ]; then + echo "$COMMAND: you must include a Category: field in your report." + else + echo "$COMMAND: \`$CATEGORY' is not a known category." + fi + fi + # + # 5) Class + # + PATTERN=">Class:" + CLASS=`eval sed -n -e "\"$SED_CMD\"" $TEMP` + case "$CLASS" in + ""|sw-bug|doc-bug|change-request|support) CNT=`expr $CNT + 1` ;; + *) echo "$COMMAND: \`$CLASS' is not a valid value for \`Class'." + esac + + [ $CNT -lt 5 -a -z "$BATCH" ] && + echo "Errors were found with the problem report." + + while true; do + if [ -z "$BATCH" ]; then + $ECHON1 "a)bort, e)dit or s)end? $ECHON2" + read input + else + if [ $CNT -eq 5 ]; then + input=s + else + input=a + fi + fi + case "$input" in + a*) + if [ -z "$BATCH" ]; then + echo "$COMMAND: the problem report remains in $BAD and is not sent." + mv $TEMP $BAD + else + echo "$COMMAND: the problem report is not sent." + fi + xs=1; exit + ;; + e*) + eval $EDIT $TEMP + continue 2 + ;; + s*) + break 2 + ;; + esac + done +done +# +# Remove comments and send the problem report +# (we have to use patterns, where the comment contains regex chars) +# +# /^>Originator:/s;$ORIGINATOR;; +sed -e " +/^SEND-PR:/d +/^>Organization:/,/^>[A-Za-z-]*:/s;$ORGANIZATION_C;; +/^>Confidential:/s;<.*>;; +/^>Synopsis:/s;$SYNOPSIS_C;; +/^>Severity:/s;<.*>;; +/^>Priority:/s;<.*>;; +/^>Category:/s;$CATEGORY_C;; +/^>Class:/s;<.*>;; +/^>Release:/,/^>[A-Za-z-]*:/s;$RELEASE_C;; +/^>Environment:/,/^>[A-Za-z-]*:/s;$ENVIRONMENT_C;; +/^>Description:/,/^>[A-Za-z-]*:/s;$DESCRIPTION_C;; +/^>How-To-Repeat:/,/^>[A-Za-z-]*:/s;$HOW_TO_REPEAT_C;; +/^>Fix:/,/^>[A-Za-z-]*:/s;$FIX_C;; +" $TEMP > $REF + +if $MAIL_AGENT < $REF; then + echo "$COMMAND: problem report sent" + xs=0; exit +else + echo "$COMMAND: mysterious mail failure." + if [ -z "$BATCH" ]; then + echo "$COMMAND: the problem report remains in $BAD and is not sent." + mv $REF $BAD + else + echo "$COMMAND: the problem report is not sent." + fi + xs=1; exit +fi diff --git a/contrib/cvs-1.12.9/src/cvsrc.c b/contrib/cvs-1.12.9/src/cvsrc.c new file mode 100644 index 0000000000..c9b4177460 --- /dev/null +++ b/contrib/cvs-1.12.9/src/cvsrc.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1993 david d zuhn + * + * Written by david d `zoo' zuhn while at Cygnus Support + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + */ + + +#include "cvs.h" +#include "getline.h" + +/* this file is to be found in the user's home directory */ + +#ifndef CVSRC_FILENAME +#define CVSRC_FILENAME ".cvsrc" +#endif +char cvsrc[] = CVSRC_FILENAME; + +#define GROW 10 + +/* Read cvsrc, processing options matching CMDNAME ("cvs" for global + options, and update *ARGC and *ARGV accordingly. */ + +void +read_cvsrc (int *argc, char ***argv, const char *cmdname) +{ + char *homedir; + char *homeinit; + FILE *cvsrcfile; + + char *line; + int line_length; + size_t line_chars_allocated; + + char *optstart; + + int command_len; + int found = 0; + + int i; + + int new_argc; + int max_new_argv; + char **new_argv; + + /* old_argc and old_argv hold the values returned from the + previous invocation of read_cvsrc and are used to free the + allocated memory. The first invocation of read_cvsrc gets argv + from the system, this memory must not be free'd. */ + static int old_argc = 0; + static char **old_argv = NULL; + + /* don't do anything if argc is -1, since that implies "help" mode */ + if (*argc == -1) + return; + + /* determine filename for ~/.cvsrc */ + + homedir = get_homedir (); + /* If we can't find a home directory, ignore ~/.cvsrc. This may + make tracking down problems a bit of a pain, but on the other + hand it might be obnoxious to complain when CVS will function + just fine without .cvsrc (and many users won't even know what + .cvsrc is). */ + if (!homedir) + return; + + homeinit = strcat_filename_onto_homedir (homedir, cvsrc); + + /* if it can't be read, there's no point to continuing */ + + if (!isreadable (homeinit)) + { + free (homeinit); + return; + } + + /* now scan the file until we find the line for the command in question */ + + line = NULL; + line_chars_allocated = 0; + command_len = strlen (cmdname); + cvsrcfile = open_file (homeinit, "r"); + while ((line_length = getline (&line, &line_chars_allocated, cvsrcfile)) + >= 0) + { + /* skip over comment lines */ + if (line[0] == '#') + continue; + + /* stop if we match the current command */ + if (!strncmp (line, cmdname, command_len) + && isspace ((unsigned char) *(line + command_len))) + { + found = 1; + break; + } + } + + if (line_length < 0 && !feof (cvsrcfile)) + error (0, errno, "cannot read %s", homeinit); + + 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 */ + for (optstart = strtok (line + command_len, "\t \n"); + optstart; + optstart = strtok (NULL, "\t \n")) + { + new_argv [new_argc++] = xstrdup (optstart); + + if (new_argc >= max_new_argv) + { + max_new_argv += GROW; + new_argv = (char **) xrealloc (new_argv, max_new_argv * sizeof (char*)); + } + } + } + + if (line != NULL) + free (line); + + /* 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++] = xstrdup ((*argv)[i]); + } + + if (old_argv != NULL) + { + /* Free the memory which was allocated in the previous + read_cvsrc call. */ + free_names (&old_argc, old_argv); + } + + old_argc = *argc = new_argc; + old_argv = *argv = new_argv; + + free (homeinit); + return; +} diff --git a/contrib/cvs-1.12.9/src/diff.c b/contrib/cvs-1.12.9/src/diff.c new file mode 100644 index 0000000000..6d55cb1904 --- /dev/null +++ b/contrib/cvs-1.12.9/src/diff.c @@ -0,0 +1,1098 @@ +/* + * 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 source distribution. + * + * Difference + * + * Run diff against versions in the repository. Options that are specified are + * passed on directly to "rcsdiff". + * + * Without any file arguments, runs diff against all the currently modified + * files. + */ + +#include "cvs.h" + +enum diff_file +{ + DIFF_ERROR, + DIFF_ADDED, + DIFF_REMOVED, + DIFF_DIFFERENT, + DIFF_SAME +}; + +static Dtype diff_dirproc (void *callerdat, const char *dir, + const char *pos_repos, const char *update_dir, + List *entries); +static int diff_filesdoneproc (void *callerdat, int err, + const char *repos, const char *update_dir, + List *entries); +static int diff_dirleaveproc (void *callerdat, const char *dir, + int err, const char *update_dir, + List *entries); +static enum diff_file diff_file_nodiff (struct file_info *finfo, Vers_TS *vers, + enum diff_file, char **rev1_cache ); +static int diff_fileproc (void *callerdat, struct file_info *finfo); +static void diff_mark_errors (int err); + + +/* Global variables. Would be cleaner if we just put this stuff in a + struct like log.c does. */ + +/* Command line tags, from -r option. Points into argv. */ +static char *diff_rev1, *diff_rev2; +/* Command line dates, from -D option. Malloc'd. */ +static char *diff_date1, *diff_date2; +static char *use_rev1, *use_rev2; +static int have_rev1_label, have_rev2_label; + +/* Revision of the user file, if it is unchanged from something in the + repository and we want to use that fact. */ +static char *user_file_rev; + +static char *options; +static char *opts; +static size_t opts_allocated = 1; +static int diff_errors; +static int empty_files = 0; + +static const char *const diff_usage[] = +{ + "Usage: %s %s [-lR] [-k kopt] [format_options]\n", + " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n", + "\t-l\tLocal directory only, not recursive\n", + "\t-R\tProcess directories recursively.\n", + "\t-k kopt\tSpecify keyword expansion mode.\n", + "\t-D d1\tDiff revision for date against working file.\n", + "\t-D d2\tDiff rev1/date1 against date2.\n", + "\t-r rev1\tDiff revision for rev1 against working file.\n", + "\t-r rev2\tDiff rev1/date1 against rev2.\n", + "\nformat_options:\n", + " -i --ignore-case Consider upper- and lower-case to be the same.\n", + " -w --ignore-all-space Ignore all white space.\n", + " -b --ignore-space-change Ignore changes in the amount of white space.\n", + " -B --ignore-blank-lines Ignore changes whose lines are all blank.\n", + " -I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.\n", + " --binary Read and write data in binary mode.\n", + " -a --text Treat all files as text.\n\n", + " -c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.\n", + " -u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.\n", + " -NUM Use NUM context lines.\n", + " -L LABEL --label LABEL Use LABEL instead of file name.\n", + " -p --show-c-function Show which C function each change is in.\n", + " -F RE --show-function-line=RE Show the most recent line matching RE.\n", + " --brief Output only whether files differ.\n", + " -e --ed Output an ed script.\n", + " -f --forward-ed Output something like an ed script in forward order.\n", + " -n --rcs Output an RCS format diff.\n", + " -y --side-by-side Output in two columns.\n", + " -W NUM --width=NUM Output at most NUM (default 130) characters per line.\n", + " --left-column Output only the left column of common lines.\n", + " --suppress-common-lines Do not output common lines.\n", + " --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.\n", + " --GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.\n", + " --line-format=LFMT Similar, but format all input lines with LFMT.\n", + " --LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.\n", + " LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.\n", + " GFMT may contain:\n", + " %%< lines from FILE1\n", + " %%> lines from FILE2\n", + " %%= lines common to FILE1 and FILE2\n", + " %%[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n", + " LETTERs are as follows for new group, lower case for old group:\n", + " F first line number\n", + " L last line number\n", + " N number of lines = L-F+1\n", + " E F-1\n", + " M L+1\n", + " LFMT may contain:\n", + " %%L contents of line\n", + " %%l contents of line, excluding any trailing newline\n", + " %%[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number\n", + " Either GFMT or LFMT may contain:\n", + " %%%% %%\n", + " %%c'C' the single character C\n", + " %%c'\\OOO' the character with octal code OOO\n\n", + " -t --expand-tabs Expand tabs to spaces in output.\n", + " -T --initial-tab Make tabs line up by prepending a tab.\n\n", + " -N --new-file Treat absent files as empty.\n", + " -s --report-identical-files Report when two files are the same.\n", + " --horizon-lines=NUM Keep NUM lines of the common prefix and suffix.\n", + " -d --minimal Try hard to find a smaller set of changes.\n", + " -H --speed-large-files Assume large files and many scattered small changes.\n", + "\n(Specify the --help global option for a list of other help options)\n", + NULL +}; + +/* I copied this array directly out of diff.c in diffutils 2.7, after + removing the following entries, none of which seem relevant to use + with CVS: + --help + --version (-v) + --recursive (-r) + --unidirectional-new-file (-P) + --starting-file (-S) + --exclude (-x) + --exclude-from (-X) + --sdiff-merge-assist + --paginate (-l) (doesn't work with library callbacks) + + I changed the options which take optional arguments (--context and + --unified) to return a number rather than a letter, so that the + optional argument could be handled more easily. I changed the + --brief and --ifdef options to return numbers, since -q and -D mean + something else to cvs diff. + + The numbers 129- that appear in the fourth element of some entries + tell the big switch in `diff' how to process those options. -- Ian + + The following options, which diff lists as "An alias, no longer + recommended" have been removed: --file-label --entire-new-file + --ascii --print. */ + +static struct option const longopts[] = +{ + {"ignore-blank-lines", 0, 0, 'B'}, + {"context", 2, 0, 143}, + {"ifdef", 1, 0, 131}, + {"show-function-line", 1, 0, 'F'}, + {"speed-large-files", 0, 0, 'H'}, + {"ignore-matching-lines", 1, 0, 'I'}, + {"label", 1, 0, 'L'}, + {"new-file", 0, 0, 'N'}, + {"initial-tab", 0, 0, 'T'}, + {"width", 1, 0, 'W'}, + {"text", 0, 0, 'a'}, + {"ignore-space-change", 0, 0, 'b'}, + {"minimal", 0, 0, 'd'}, + {"ed", 0, 0, 'e'}, + {"forward-ed", 0, 0, 'f'}, + {"ignore-case", 0, 0, 'i'}, + {"rcs", 0, 0, 'n'}, + {"show-c-function", 0, 0, 'p'}, + + /* This is a potentially very useful option, except the output is so + silly. It would be much better for it to look like "cvs rdiff -s" + which displays all the same info, minus quite a few lines of + extraneous garbage. */ + {"brief", 0, 0, 145}, + + {"report-identical-files", 0, 0, 's'}, + {"expand-tabs", 0, 0, 't'}, + {"ignore-all-space", 0, 0, 'w'}, + {"side-by-side", 0, 0, 'y'}, + {"unified", 2, 0, 146}, + {"left-column", 0, 0, 129}, + {"suppress-common-lines", 0, 0, 130}, + {"old-line-format", 1, 0, 132}, + {"new-line-format", 1, 0, 133}, + {"unchanged-line-format", 1, 0, 134}, + {"line-format", 1, 0, 135}, + {"old-group-format", 1, 0, 136}, + {"new-group-format", 1, 0, 137}, + {"unchanged-group-format", 1, 0, 138}, + {"changed-group-format", 1, 0, 139}, + {"horizon-lines", 1, 0, 140}, + {"binary", 0, 0, 142}, + {0, 0, 0, 0} +}; + +/* CVS 1.9 and similar versions seemed to have pretty weird handling + of -y and -T. In the cases where it called rcsdiff, + they would have the meanings mentioned below. In the cases where it + called diff, they would have the meanings mentioned in "longopts". + Noone seems to have missed them, so I think the right thing to do is + just to remove the options altogether (which I have done). + + In the case of -z and -q, "cvs diff" did not accept them even back + when we called rcsdiff (at least, it hasn't accepted them + recently). + + In comparing rcsdiff to the new CVS implementation, I noticed that + the following rcsdiff flags are not handled by CVS diff: + + -y: perform diff even when the requested revisions are the + same revision number + -q: run quietly + -T: preserve modification time on the RCS file + -z: specify timezone for use in file labels + + I think these are not really relevant. -y is undocumented even in + RCS 5.7, and seems like a minor change at best. According to RCS + documentation, -T only applies when a RCS file has been modified + because of lock changes; doesn't CVS sidestep RCS's entire lock + structure? -z seems to be unsupported by CVS diff, and has a + different meaning as a global option anyway. (Adding it could be + a feature, but if it is left out for now, it should not break + anything.) For the purposes of producing output, CVS diff appears + mostly to ignore -q. Maybe this should be fixed, but I think it's + a larger issue than the changes included here. */ + +int +diff (int argc, char **argv) +{ + char tmp[50]; + int c, err = 0; + int local = 0; + int which; + int option_index; + + if (argc == -1) + usage (diff_usage); + + have_rev1_label = have_rev2_label = 0; + + /* + * Note that we catch all the valid arguments here, so that we can + * intercept the -r arguments for doing revision diffs; and -l/-R for a + * non-recursive/recursive diff. + */ + + /* Clean out our global variables (multiroot can call us multiple + times and the server can too, if the client sends several + diff commands). */ + if (opts == NULL) + { + opts_allocated = 1; + opts = xmalloc (opts_allocated); + } + opts[0] = '\0'; + diff_rev1 = NULL; + diff_rev2 = NULL; + diff_date1 = NULL; + diff_date2 = NULL; + + optind = 0; + /* FIXME: This should really be allocating an argv to be passed to diff + * later rather than strcatting onto the opts variable. We have some + * handling routines that can already handle most of the argc/argv + * maintenance for us and currently, if anyone were to attempt to pass a + * quoted string in here, it would be split on spaces and tabs on its way + * to diff. + */ + while ((c = getopt_long (argc, argv, + "+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:W:k:r:", + longopts, &option_index)) != -1) + { + switch (c) + { + case 'y': + xrealloc_and_strcat (&opts, &opts_allocated, " --side-by-side"); + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'h': case 'i': case 'n': case 'p': case 's': case 't': + case 'u': case 'w': + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + case 'B': case 'H': case 'T': + (void) sprintf (tmp, " -%c", (char) c); + xrealloc_and_strcat (&opts, &opts_allocated, tmp); + break; + case 'L': + if (have_rev1_label++) + if (have_rev2_label++) + { + error (0, 0, "extra -L arguments ignored"); + break; + } + + xrealloc_and_strcat (&opts, &opts_allocated, " -L"); + xrealloc_and_strcat (&opts, &opts_allocated, optarg); + break; + case 'C': case 'F': case 'I': case 'U': case 'W': + (void) sprintf (tmp, " -%c", (char) c); + xrealloc_and_strcat (&opts, &opts_allocated, tmp); + xrealloc_and_strcat (&opts, &opts_allocated, optarg); + break; + case 131: + /* --ifdef. */ + xrealloc_and_strcat (&opts, &opts_allocated, " --ifdef="); + xrealloc_and_strcat (&opts, &opts_allocated, optarg); + break; + case 129: case 130: case 132: case 133: case 134: + case 135: case 136: case 137: case 138: case 139: case 140: + case 141: case 142: case 143: case 145: case 146: + xrealloc_and_strcat (&opts, &opts_allocated, " --"); + xrealloc_and_strcat (&opts, &opts_allocated, + longopts[option_index].name); + if (longopts[option_index].has_arg == 1 + || (longopts[option_index].has_arg == 2 + && optarg != NULL)) + { + xrealloc_and_strcat (&opts, &opts_allocated, "="); + xrealloc_and_strcat (&opts, &opts_allocated, optarg); + } + break; + case 'R': + local = 0; + break; + case 'l': + local = 1; + break; + case 'k': + if (options) + free (options); + options = RCS_check_kflag (optarg); + break; + case 'r': + if (diff_rev2 != NULL || diff_date2 != NULL) + error (1, 0, + "no more than two revisions/dates can be specified"); + if (diff_rev1 != NULL || diff_date1 != NULL) + diff_rev2 = optarg; + else + diff_rev1 = optarg; + break; + case 'D': + if (diff_rev2 != NULL || diff_date2 != NULL) + error (1, 0, + "no more than two revisions/dates can be specified"); + if (diff_rev1 != NULL || diff_date1 != NULL) + diff_date2 = Make_Date (optarg); + else + diff_date1 = Make_Date (optarg); + break; + case 'N': + empty_files = 1; + break; + case '?': + default: + usage (diff_usage); + break; + } + } + argc -= optind; + argv += optind; + + /* make sure options is non-null */ + if (!options) + options = xstrdup (""); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) { + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (local) + send_arg("-l"); + if (empty_files) + send_arg("-N"); + send_option_string (opts); + if (options[0] != '\0') + send_arg (options); + if (diff_rev1) + option_with_arg ("-r", diff_rev1); + if (diff_date1) + client_senddate (diff_date1); + if (diff_rev2) + option_with_arg ("-r", diff_rev2); + if (diff_date2) + client_senddate (diff_date2); + send_arg ("--"); + + /* Send the current files unless diffing two revs from the archive */ + if (diff_rev2 == NULL && diff_date2 == NULL) + send_files (argc, argv, local, 0, 0); + else + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); + + send_file_names (argc, argv, SEND_EXPAND_WILD); + + send_to_server ("diff\012", 0); + err = get_responses_and_close (); + free (options); + options = NULL; + return (err); + } +#endif + + if (diff_rev1 != NULL) + tag_check_valid (diff_rev1, argc, argv, local, 0, ""); + if (diff_rev2 != NULL) + tag_check_valid (diff_rev2, argc, argv, local, 0, ""); + + which = W_LOCAL; + if (diff_rev1 != NULL || diff_date1 != NULL) + which |= W_REPOS | W_ATTIC; + + wrap_setup (); + + /* start the recursion processor */ + err = start_recursion + ( diff_fileproc, diff_filesdoneproc, diff_dirproc, + diff_dirleaveproc, NULL, argc, argv, local, + which, 0, CVS_LOCK_READ, (char *) NULL, 1, (char *) NULL ); + + /* clean up */ + free (options); + options = NULL; + + if (diff_date1 != NULL) + free (diff_date1); + if (diff_date2 != NULL) + free (diff_date2); + + return (err); +} + +/* + * Do a file diff + */ +/* ARGSUSED */ +static int +diff_fileproc (void *callerdat, struct file_info *finfo) +{ + int status, err = 2; /* 2 == trouble, like rcsdiff */ + Vers_TS *vers; + enum diff_file empty_file = DIFF_DIFFERENT; + char *tmp = NULL; + char *tocvsPath = NULL; + char *fname = NULL; + char *label1; + char *label2; + char *rev1_cache = NULL; + + user_file_rev = 0; + vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0); + + if (diff_rev2 != NULL || diff_date2 != NULL) + { + /* Skip all the following checks regarding the user file; we're + not using it. */ + } + else if (vers->vn_user == NULL) + { + /* The file does not exist in the working directory. */ + if ((diff_rev1 != NULL || diff_date1 != NULL) + && vers->srcfile != NULL) + { + /* The file does exist in the repository. */ + if (empty_files) + empty_file = DIFF_REMOVED; + else + { + int exists; + + exists = 0; + /* special handling for TAG_HEAD */ + if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0) + { + char *head = + (vers->vn_rcs == NULL + ? NULL + : RCS_branch_head (vers->srcfile, vers->vn_rcs)); + exists = head != NULL && !RCS_isdead(vers->srcfile, head); + if (head != NULL) + free (head); + } + else + { + Vers_TS *xvers; + + xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, + 1, 0); + exists = xvers->vn_rcs != NULL && !RCS_isdead(xvers->srcfile, xvers->vn_rcs); + freevers_ts (&xvers); + } + if (exists) + error (0, 0, + "%s no longer exists, no comparison available", + finfo->fullname); + goto out; + } + } + else + { + error (0, 0, "I know nothing about %s", finfo->fullname); + goto out; + } + } + else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') + { + /* The file was added locally. */ + int exists = 0; + + if (vers->srcfile != NULL) + { + /* The file does exist in the repository. */ + + if ((diff_rev1 != NULL || diff_date1 != NULL)) + { + /* special handling for TAG_HEAD */ + if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0) + { + char *head = + (vers->vn_rcs == NULL + ? NULL + : RCS_branch_head (vers->srcfile, vers->vn_rcs)); + exists = head != NULL && !RCS_isdead(vers->srcfile, head); + if (head != NULL) + free (head); + } + else + { + Vers_TS *xvers; + + xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, + 1, 0); + exists = xvers->vn_rcs != NULL + && !RCS_isdead (xvers->srcfile, xvers->vn_rcs); + freevers_ts (&xvers); + } + } + else + { + /* The file was added locally, but an RCS archive exists. Our + * base revision must be dead. + */ + /* No need to set, exists = 0, here. That's the default. */ + } + } + if (!exists) + { + /* If we got here, then either the RCS archive does not exist or + * the relevant revision is dead. + */ + if (empty_files) + empty_file = DIFF_ADDED; + else + { + error (0, 0, "%s is a new entry, no comparison available", + finfo->fullname); + goto out; + } + } + } + else if (vers->vn_user[0] == '-') + { + if (empty_files) + empty_file = DIFF_REMOVED; + else + { + error (0, 0, "%s was removed, no comparison available", + finfo->fullname); + goto out; + } + } + else + { + if (vers->vn_rcs == NULL && vers->srcfile == NULL) + { + error (0, 0, "cannot find revision control file for %s", + finfo->fullname); + goto out; + } + else + { + if (vers->ts_user == NULL) + { + error (0, 0, "cannot find %s", finfo->fullname); + goto out; + } + else if (!strcmp (vers->ts_user, vers->ts_rcs)) + { + /* The user file matches some revision in the repository + Diff against the repository (for remote CVS, we might not + have a copy of the user file around). */ + user_file_rev = vers->vn_user; + } + } + } + + empty_file = diff_file_nodiff( finfo, vers, empty_file, &rev1_cache ); + if( empty_file == DIFF_SAME ) + { + /* In the server case, would be nice to send a "Checked-in" + response, so that the client can rewrite its timestamp. + server_checked_in by itself isn't the right thing (it + needs a server_register), but I'm not sure what is. + It isn't clear to me how "cvs status" handles this (that + is, for a client which sends Modified not Is-modified to + "cvs status"), but it does. */ + err = 0; + goto out; + } + else if( empty_file == DIFF_ERROR ) + goto out; + + /* Output an "Index:" line for patch to use */ + cvs_output ("Index: ", 0); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); + + tocvsPath = wrap_tocvs_process_file(finfo->file); + if( tocvsPath != NULL ) + { + /* Backup the current version of the file to CVS/,,filename */ + fname = xmalloc (strlen (finfo->file) + + sizeof CVSADM + + sizeof CVSPREFIX + + 10); + sprintf(fname,"%s/%s%s",CVSADM, CVSPREFIX, finfo->file); + if (unlink_file_dir (fname) < 0) + if (! existence_error (errno)) + error (1, errno, "cannot remove %s", fname); + rename_file (finfo->file, fname); + /* Copy the wrapped file to the current directory then go to work */ + copy_file (tocvsPath, finfo->file); + } + + /* Set up file labels appropriate for compatibility with the Larry Wall + * implementation of patch if the user didn't specify. This is irrelevant + * according to the POSIX.2 specification. + */ + label1 = NULL; + label2 = NULL; + /* The user cannot set the rev2 label without first setting the rev1 + * label. + */ + if (!have_rev2_label) + { + if (empty_file == DIFF_REMOVED) + label2 = make_file_label (DEVNULL, NULL, NULL); + else + label2 = make_file_label (finfo->fullname, use_rev2, + vers ? vers->srcfile : NULL); + if (!have_rev1_label) + { + if (empty_file == DIFF_ADDED) + label1 = make_file_label (DEVNULL, NULL, NULL); + else + label1 = make_file_label (finfo->fullname, use_rev1, + vers ? vers->srcfile : NULL); + } + } + + if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED) + { + /* This is fullname, not file, possibly despite the POSIX.2 + * specification, because that's the way all the Larry Wall + * implementations of patch (are there other implementations?) want + * things and the POSIX.2 spec appears to leave room for this. + */ + cvs_output ("\ +===================================================================\n\ +RCS file: ", 0); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); + + cvs_output ("diff -N ", 0); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); + + if (empty_file == DIFF_ADDED) + { + if (use_rev2 == NULL) + status = diff_exec (DEVNULL, finfo->file, label1, label2, opts, + RUN_TTY); + else + { + int retcode; + + tmp = cvs_temp_name (); + retcode = RCS_checkout (vers->srcfile, (char *) NULL, + use_rev2, (char *) NULL, + (*options + ? options + : vers->options), + tmp, (RCSCHECKOUTPROC) NULL, + (void *) NULL); + if( retcode != 0 ) + goto out; + + status = diff_exec (DEVNULL, tmp, label1, label2, opts, RUN_TTY); + } + } + else + { + int retcode; + + tmp = cvs_temp_name (); + retcode = RCS_checkout (vers->srcfile, (char *) NULL, + use_rev1, (char *) NULL, + *options ? options : vers->options, + tmp, (RCSCHECKOUTPROC) NULL, + (void *) NULL); + if (retcode != 0) + goto out; + + status = diff_exec (tmp, DEVNULL, label1, label2, opts, RUN_TTY); + } + } + else + { + status = RCS_exec_rcsdiff(vers->srcfile, opts, + *options ? options : vers->options, + use_rev1, rev1_cache, use_rev2, + label1, label2, + finfo->file); + + } + + if (label1) free (label1); + if (label2) free (label2); + + switch (status) + { + case -1: /* fork failed */ + error (1, errno, "fork failed while diffing %s", + vers->srcfile->path); + case 0: /* everything ok */ + err = 0; + break; + default: /* other error */ + err = status; + break; + } + +out: + if( tocvsPath != NULL ) + { + if (unlink_file_dir (finfo->file) < 0) + if (! existence_error (errno)) + error (1, errno, "cannot remove %s", finfo->file); + + rename_file (fname, finfo->file); + if (unlink_file (tocvsPath) < 0) + error (1, errno, "cannot remove %s", tocvsPath); + free (fname); + } + + /* Call CVS_UNLINK() rather than unlink_file() below to avoid the check + * for noexec. + */ + if( tmp != NULL ) + { + if (CVS_UNLINK(tmp) < 0) + error (0, errno, "cannot remove %s", tmp); + free (tmp); + } + if( rev1_cache != NULL ) + { + if( CVS_UNLINK( rev1_cache ) < 0 ) + error( 0, errno, "cannot remove %s", rev1_cache ); + free( rev1_cache ); + } + + freevers_ts (&vers); + diff_mark_errors (err); + return err; +} + +/* + * Remember the exit status for each file. + */ +static void +diff_mark_errors (int err) +{ + if (err > diff_errors) + diff_errors = err; +} + + + +/* + * Print a warm fuzzy message when we enter a dir + * + * Don't try to diff directories that don't exist! -- DW + */ +/* ARGSUSED */ +static Dtype +diff_dirproc (void *callerdat, const char *dir, const char *pos_repos, + const char *update_dir, List *entries) +{ + /* XXX - check for dirs we don't want to process??? */ + + /* YES ... for instance dirs that don't exist!!! -- DW */ + if (!isdir (dir)) + return (R_SKIP_ALL); + + if (!quiet) + error (0, 0, "Diffing %s", update_dir); + return (R_PROCESS); +} + + + +/* + * Concoct the proper exit status - done with files + */ +/* ARGSUSED */ +static int +diff_filesdoneproc (void *callerdat, int err, const char *repos, + const char *update_dir, List *entries) +{ + return (diff_errors); +} + + + +/* + * Concoct the proper exit status - leaving directories + */ +/* ARGSUSED */ +static int +diff_dirleaveproc (void *callerdat, const char *dir, int err, + const char *update_dir, List *entries) +{ + return (diff_errors); +} + + + +/* + * verify that a file is different + */ +static enum diff_file +diff_file_nodiff(struct file_info *finfo, Vers_TS *vers, enum diff_file empty_file, char **rev1_cache) + + + + /* Cache the content of rev1 if we have to look + * it up. + */ +{ + Vers_TS *xvers; + int retcode; + + TRACE (TRACE_FUNCTION, "diff_file_nodiff (%s, %d)", + finfo->fullname ? finfo->fullname : "(null)", empty_file); + + /* free up any old use_rev* variables and reset 'em */ + if (use_rev1) + free (use_rev1); + if (use_rev2) + free (use_rev2); + use_rev1 = use_rev2 = (char *) NULL; + + if (diff_rev1 || diff_date1) + { + /* special handling for TAG_HEAD */ + if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0) + { + if (vers->vn_rcs != NULL && vers->srcfile != NULL) + use_rev1 = RCS_branch_head (vers->srcfile, vers->vn_rcs); + } + else + { + xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0); + if (xvers->vn_rcs != NULL) + use_rev1 = xstrdup (xvers->vn_rcs); + freevers_ts (&xvers); + } + } + if (diff_rev2 || diff_date2) + { + /* special handling for TAG_HEAD */ + if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0) + { + if (vers->vn_rcs != NULL && vers->srcfile != NULL) + use_rev2 = RCS_branch_head (vers->srcfile, vers->vn_rcs); + } + else + { + xvers = Version_TS (finfo, NULL, diff_rev2, diff_date2, 1, 0); + if (xvers->vn_rcs != NULL) + use_rev2 = xstrdup (xvers->vn_rcs); + freevers_ts (&xvers); + } + + if( use_rev1 == NULL || RCS_isdead( vers->srcfile, use_rev1 ) ) + { + /* The first revision does not exist. If EMPTY_FILES is + true, treat this as an added file. Otherwise, warn + about the missing tag. */ + if( use_rev2 == NULL || RCS_isdead( vers->srcfile, use_rev2 ) ) + /* At least in the case where DIFF_REV1 and DIFF_REV2 + * are both numeric (and non-existant (NULL), as opposed to + * dead?), we should be returning some kind of error (see + * basicb-8a0 in testsuite). The symbolic case may be more + * complicated. + */ + return DIFF_SAME; + if( empty_files ) + return DIFF_ADDED; + if( use_rev1 != NULL ) + { + if (diff_rev1) + { + error( 0, 0, + "Tag %s refers to a dead (removed) revision in file `%s'.", + diff_rev1, finfo->fullname ); + } + else + { + error( 0, 0, + "Date %s refers to a dead (removed) revision in file `%s'.", + diff_date1, finfo->fullname ); + } + error( 0, 0, + "No comparison available. Pass `-N' to `%s diff'?", + program_name ); + } + else if (diff_rev1) + error (0, 0, "tag %s is not in file %s", diff_rev1, + finfo->fullname); + else + error (0, 0, "no revision for date %s in file %s", + diff_date1, finfo->fullname); + return DIFF_ERROR; + } + + assert( use_rev1 != NULL ); + if( use_rev2 == NULL || RCS_isdead( vers->srcfile, use_rev2 ) ) + { + /* The second revision does not exist. If EMPTY_FILES is + true, treat this as a removed file. Otherwise warn + about the missing tag. */ + if (empty_files) + return DIFF_REMOVED; + if( use_rev2 != NULL ) + { + if (diff_rev2) + { + error( 0, 0, + "Tag %s refers to a dead (removed) revision in file `%s'.", + diff_rev2, finfo->fullname ); + } + else + { + error( 0, 0, + "Date %s refers to a dead (removed) revision in file `%s'.", + diff_date2, finfo->fullname ); + } + error( 0, 0, + "No comparison available. Pass `-N' to `%s diff'?", + program_name ); + } + else if (diff_rev2) + error (0, 0, "tag %s is not in file %s", diff_rev2, + finfo->fullname); + else + error (0, 0, "no revision for date %s in file %s", + diff_date2, finfo->fullname); + return DIFF_ERROR; + } + /* Now, see if we really need to do the diff. We can't assume that the + * files are different when the revs are. + */ + assert( use_rev2 != NULL ); + if( strcmp (use_rev1, use_rev2) == 0 ) + return DIFF_SAME; + /* else fall through and do the diff */ + } + + /* If we had a r1/d1 & r2/d2, then at this point we must have a C3P0... + * err... ok, then both rev1 & rev2 must have resolved to an existing, + * live version due to if statement we just closed. + */ + assert (!(diff_rev2 || diff_date2) || (use_rev1 && use_rev2)); + + if ((diff_rev1 || diff_date1) && + (use_rev1 == NULL || RCS_isdead (vers->srcfile, use_rev1))) + { + /* The first revision does not exist, and no second revision + was given. */ + if (empty_files) + { + if (empty_file == DIFF_REMOVED) + return DIFF_SAME; + if( user_file_rev && use_rev2 == NULL ) + use_rev2 = xstrdup( user_file_rev ); + return DIFF_ADDED; + } + if( use_rev1 != NULL ) + { + if (diff_rev1) + { + error( 0, 0, + "Tag %s refers to a dead (removed) revision in file `%s'.", + diff_rev1, finfo->fullname ); + } + else + { + error( 0, 0, + "Date %s refers to a dead (removed) revision in file `%s'.", + diff_date1, finfo->fullname ); + } + error( 0, 0, + "No comparison available. Pass `-N' to `%s diff'?", + program_name ); + } + else if ( diff_rev1 ) + error( 0, 0, "tag %s is not in file %s", diff_rev1, + finfo->fullname ); + else + error( 0, 0, "no revision for date %s in file %s", + diff_date1, finfo->fullname ); + return DIFF_ERROR; + } + + assert( !diff_rev1 || use_rev1 ); + + if (user_file_rev) + { + /* drop user_file_rev into first unused use_rev */ + if (!use_rev1) + use_rev1 = xstrdup (user_file_rev); + else if (!use_rev2) + use_rev2 = xstrdup (user_file_rev); + /* and if not, it wasn't needed anyhow */ + user_file_rev = NULL; + } + + /* Now, see if we really need to do the diff. We can't assume that the + * files are different when the revs are. + */ + if( use_rev1 && use_rev2) + { + if (strcmp (use_rev1, use_rev2) == 0) + return DIFF_SAME; + /* Fall through and do the diff. */ + } + /* Don't want to do the timestamp check with both use_rev1 & use_rev2 set. + * The timestamp check is just for the default case of diffing the + * workspace file against its base revision. + */ + else if( use_rev1 == NULL + || ( vers->vn_user != NULL + && strcmp( use_rev1, vers->vn_user ) == 0 ) ) + { + if (empty_file == DIFF_DIFFERENT + && vers->ts_user != NULL + && strcmp (vers->ts_rcs, vers->ts_user) == 0 + && (!(*options) || strcmp (options, vers->options) == 0)) + { + return DIFF_SAME; + } + if (use_rev1 == NULL + && (vers->vn_user[0] != '0' || vers->vn_user[1] != '\0')) + { + if (vers->vn_user[0] == '-') + use_rev1 = xstrdup (vers->vn_user + 1); + else + use_rev1 = xstrdup (vers->vn_user); + } + } + + /* If we already know that the file is being added or removed, + then we don't want to do an actual file comparison here. */ + if (empty_file != DIFF_DIFFERENT) + return empty_file; + + /* + * Run a quick cmp to see if we should bother with a full diff. + */ + + retcode = RCS_cmp_file( vers->srcfile, use_rev1, rev1_cache, + use_rev2, *options ? options : vers->options, + finfo->file ); + + return retcode == 0 ? DIFF_SAME : DIFF_DIFFERENT; +} diff --git a/contrib/cvs-1.12.9/src/edit.c b/contrib/cvs-1.12.9/src/edit.c new file mode 100644 index 0000000000..9b1d81228a --- /dev/null +++ b/contrib/cvs-1.12.9/src/edit.c @@ -0,0 +1,1094 @@ +/* Implementation for "cvs edit", "cvs watch on", and related commands + + 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" +#include "watch.h" +#include "edit.h" +#include "fileattr.h" + +static int watch_onoff (int, char **); + +static int setting_default; +static int turning_on; + +static int setting_tedit; +static int setting_tunedit; +static int setting_tcommit; + + + +static int +onoff_fileproc (void *callerdat, struct file_info *finfo) +{ + fileattr_set (finfo->file, "_watched", turning_on ? "" : NULL); + return 0; +} + + + +static int +onoff_filesdoneproc (void *callerdat, int err, const char *repository, + const char *update_dir, List *entries) +{ + if (setting_default) + fileattr_set (NULL, "_watched", turning_on ? "" : NULL); + return err; +} + + + +static int +watch_onoff (int argc, char **argv) +{ + int c; + int local = 0; + int err; + + optind = 0; + while ((c = getopt (argc, argv, "+lR")) != -1) + { + switch (c) + { + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case '?': + default: + usage (watch_usage); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + start_server (); + + ign_setup (); + + if (local) + send_arg ("-l"); + send_arg ("--"); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server (turning_on ? "watch-on\012" : "watch-off\012", 0); + return get_responses_and_close (); + } +#endif /* CLIENT_SUPPORT */ + + setting_default = (argc <= 0); + + lock_tree_promotably (argc, argv, local, W_LOCAL, 0); + + err = start_recursion + (onoff_fileproc, onoff_filesdoneproc, + NULL, NULL, NULL, + argc, argv, local, W_LOCAL, 0, CVS_LOCK_WRITE, + NULL, 0, NULL); + + Lock_Cleanup (); + return err; +} + +int +watch_on (int argc, char **argv) +{ + turning_on = 1; + return watch_onoff (argc, argv); +} + +int +watch_off (int argc, char **argv) +{ + turning_on = 0; + return watch_onoff (argc, argv); +} + +static int dummy_fileproc (void *callerdat, struct file_info *finfo); + +static int +dummy_fileproc (void *callerdat, struct file_info *finfo) +{ + /* This is a pretty hideous hack, but the gist of it is that recurse.c + won't call notify_check unless there is a fileproc, so we can't just + pass NULL for fileproc. */ + return 0; +} + +static int ncheck_fileproc (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 + cases (e.g. foo/bar.c), even though that means checking over and over + for the same CVSADM_NOTIFY file which we removed the first time we + processed the directory. */ + +static int +ncheck_fileproc (void *callerdat, struct file_info *finfo) +{ + int notif_type; + char *filename; + char *val; + char *cp; + char *watches; + + FILE *fp; + char *line = NULL; + size_t line_len = 0; + + /* We send notifications even if noexec. I'm not sure which behavior + is most sensible. */ + + fp = CVS_FOPEN (CVSADM_NOTIFY, "r"); + if (fp == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot open %s", CVSADM_NOTIFY); + return 0; + } + + while (getline (&line, &line_len, fp) > 0) + { + notif_type = line[0]; + if (notif_type == '\0') + continue; + filename = line + 1; + cp = strchr (filename, '\t'); + if (cp == NULL) + continue; + *cp++ = '\0'; + val = cp; + cp = strchr (val, '\t'); + if (cp == NULL) + continue; + *cp++ = '+'; + cp = strchr (cp, '\t'); + if (cp == NULL) + continue; + *cp++ = '+'; + cp = strchr (cp, '\t'); + if (cp == NULL) + continue; + *cp++ = '\0'; + watches = cp; + cp = strchr (cp, '\n'); + if (cp == NULL) + continue; + *cp = '\0'; + + notify_do (notif_type, filename, getcaller (), val, watches, + finfo->repository); + } + free (line); + + if (ferror (fp)) + error (0, errno, "cannot read %s", CVSADM_NOTIFY); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", CVSADM_NOTIFY); + + if ( CVS_UNLINK (CVSADM_NOTIFY) < 0) + error (0, errno, "cannot remove %s", CVSADM_NOTIFY); + + return 0; +} + +static int send_notifications (int, char **, int); + +/* Look through the CVSADM_NOTIFY file and process each item there + accordingly. */ +static int +send_notifications (int argc, char **argv, int local) +{ + int err = 0; + +#ifdef CLIENT_SUPPORT + /* OK, we've done everything which needs to happen on the client side. + Now we can try to contact the server; if we fail, then the + notifications stay in CVSADM_NOTIFY to be sent next time. */ + if (current_parsed_root->isremote) + { + if (strcmp (cvs_cmd_name, "release") != 0) + { + start_server (); + ign_setup (); + } + + err += start_recursion + ( dummy_fileproc, (FILESDONEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, + argc, argv, local, W_LOCAL, 0, 0, (char *) NULL, + 0, (char *) NULL ); + + send_to_server ("noop\012", 0); + if (strcmp (cvs_cmd_name, "release") == 0) + err += get_server_responses (); + else + err += get_responses_and_close (); + } + else +#endif + { + /* Local. */ + + lock_tree_promotably (argc, argv, local, W_LOCAL, 0); + err += start_recursion + (ncheck_fileproc, NULL, NULL, NULL, NULL, + argc, argv, local, W_LOCAL, 0, CVS_LOCK_WRITE, + NULL, 0, NULL); + Lock_Cleanup (); + } + return err; +} + + + +static int edit_fileproc (void *callerdat, struct file_info *finfo); + +static int +edit_fileproc (void *callerdat, struct file_info *finfo) +{ + FILE *fp; + time_t now; + char *ascnow; + char *basefilename; + + if (noexec) + return 0; + + /* This is a somewhat screwy way to check for this, because it + doesn't help errors other than the nonexistence of the file + (e.g. permissions problems). It might be better to rearrange + the code so that CVSADM_NOTIFY gets written only after the + various actions succeed (but what if only some of them + succeed). */ + if (!isfile (finfo->file)) + { + error (0, 0, "no such file %s; ignored", finfo->fullname); + return 0; + } + + fp = open_file (CVSADM_NOTIFY, "a"); + + (void) time (&now); + ascnow = asctime (gmtime (&now)); + ascnow[24] = '\0'; + /* Fix non-standard format. */ + if (ascnow[8] == '0') ascnow[8] = ' '; + fprintf (fp, "E%s\t%s GMT\t%s\t%s\t", finfo->file, + ascnow, hostname, CurDir); + if (setting_tedit) + fprintf (fp, "E"); + if (setting_tunedit) + fprintf (fp, "U"); + if (setting_tcommit) + fprintf (fp, "C"); + fprintf (fp, "\n"); + + if (fclose (fp) < 0) + { + if (finfo->update_dir[0] == '\0') + error (0, errno, "cannot close %s", CVSADM_NOTIFY); + else + error (0, errno, "cannot close %s/%s", finfo->update_dir, + CVSADM_NOTIFY); + } + + xchmod (finfo->file, 1); + + /* Now stash the file away in CVSADM so that unedit can revert even if + it can't communicate with the server. We stash away a writable + 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_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, "/"); + strcat (basefilename, finfo->file); + copy_file (finfo->file, basefilename); + free (basefilename); + + { + Node *node; + + node = findnode_fn (finfo->entries, finfo->file); + if (node != NULL) + base_register (finfo, ((Entnode *) node->data)->version); + } + + return 0; +} + +static const char *const edit_usage[] = +{ + "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", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +edit (int argc, char **argv) +{ + int local = 0; + int c; + int err; + int a_omitted; + + if (argc == -1) + usage (edit_usage); + + a_omitted = 1; + setting_tedit = 0; + setting_tunedit = 0; + setting_tcommit = 0; + optind = 0; + 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) + setting_tedit = 1; + else if (strcmp (optarg, "unedit") == 0) + setting_tunedit = 1; + else if (strcmp (optarg, "commit") == 0) + setting_tcommit = 1; + else if (strcmp (optarg, "all") == 0) + { + setting_tedit = 1; + setting_tunedit = 1; + setting_tcommit = 1; + } + else if (strcmp (optarg, "none") == 0) + { + setting_tedit = 0; + setting_tunedit = 0; + setting_tcommit = 0; + } + else + usage (edit_usage); + break; + case '?': + default: + usage (edit_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (a_omitted) + { + setting_tedit = 1; + setting_tunedit = 1; + setting_tcommit = 1; + } + + if (strpbrk (hostname, "+,>;=\t\n") != NULL) + error (1, 0, + "host name (%s) contains an invalid character (+,>;=\\t\\n)", + hostname); + if (strpbrk (CurDir, "+,>;=\t\n") != NULL) + error (1, 0, +"current directory (%s) contains an invalid character (+,>;=\\t\\n)", + CurDir); + + /* No need to readlock since we aren't doing anything to the + repository. */ + err = start_recursion + ( edit_fileproc, (FILESDONEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, + argc, argv, local, W_LOCAL, 0, 0, (char *) NULL, + 0, (char *) NULL ); + + err += send_notifications (argc, argv, local); + + return err; +} + +static int unedit_fileproc (void *callerdat, struct file_info *finfo); + +static int +unedit_fileproc (void *callerdat, struct file_info *finfo) +{ + FILE *fp; + time_t now; + char *ascnow; + char *basefilename; + + if (noexec) + return 0; + + basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (finfo->file)); + strcpy (basefilename, CVSADM_BASE); + strcat (basefilename, "/"); + strcat (basefilename, finfo->file); + if (!isfile (basefilename)) + { + /* This file apparently was never cvs edit'd (e.g. we are uneditting + a directory where only some of the files were cvs edit'd. */ + free (basefilename); + return 0; + } + + if (xcmp (finfo->file, basefilename) != 0) + { + printf ("%s has been modified; revert changes? ", finfo->fullname); + if (!yesno ()) + { + /* "no". */ + free (basefilename); + return 0; + } + } + rename_file (basefilename, finfo->file); + free (basefilename); + + fp = open_file (CVSADM_NOTIFY, "a"); + + (void) time (&now); + ascnow = asctime (gmtime (&now)); + ascnow[24] = '\0'; + /* Fix non-standard format. */ + if (ascnow[8] == '0') ascnow[8] = ' '; + fprintf (fp, "U%s\t%s GMT\t%s\t%s\t\n", finfo->file, + ascnow, hostname, CurDir); + + if (fclose (fp) < 0) + { + if (finfo->update_dir[0] == '\0') + error (0, errno, "cannot close %s", CVSADM_NOTIFY); + else + error (0, errno, "cannot close %s/%s", finfo->update_dir, + CVSADM_NOTIFY); + } + + /* Now update the revision number in CVS/Entries from CVS/Baserev. + The basic idea here is that we are reverting to the revision + that the user edited. If we wanted "cvs update" to update + CVS/Base as we go along (so that an unedit could revert to the + current repository revision), we would need: + + update (or all send_files?) (client) needs to send revision in + new Entry-base request. update (server/local) needs to check + revision against repository and send new Update-base response + (like Update-existing in that the file already exists. While + we are at it, might try to clean up the syntax by having the + mode only in a "Mode" response, not in the Update-base itself). */ + { + char *baserev; + Node *node; + Entnode *entdata; + + baserev = base_get (finfo); + node = findnode_fn (finfo->entries, finfo->file); + /* The case where node is NULL probably should be an error or + something, but I don't want to think about it too hard right + now. */ + if (node != NULL) + { + entdata = node->data; + if (baserev == NULL) + { + /* This can only happen if the CVS/Baserev file got + corrupted. We suspect it might be possible if the + user interrupts CVS, although I haven't verified + that. */ + error (0, 0, "%s not mentioned in %s", finfo->fullname, + CVSADM_BASEREV); + + /* Since we don't know what revision the file derives from, + keeping it around would be asking for trouble. */ + if (unlink_file (finfo->file) < 0) + error (0, errno, "cannot remove %s", finfo->fullname); + + /* This is cheesy, in a sense; why shouldn't we do the + update for the user? However, doing that would require + contacting the server, so maybe this is OK. */ + error (0, 0, "run update to complete the unedit"); + return 0; + } + Register (finfo->entries, finfo->file, baserev, entdata->timestamp, + entdata->options, entdata->tag, entdata->date, + entdata->conflict); + } + free (baserev); + base_deregister (finfo); + } + + xchmod (finfo->file, 0); + return 0; +} + +static const char *const unedit_usage[] = +{ + "Usage: %s %s [-lR] [files...]\n", + "-l: Local directory only, not recursive\n", + "-R: Process directories recursively\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +unedit (int argc, char **argv) +{ + int local = 0; + int c; + int err; + + if (argc == -1) + usage (unedit_usage); + + optind = 0; + while ((c = getopt (argc, argv, "+lR")) != -1) + { + switch (c) + { + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case '?': + default: + usage (unedit_usage); + break; + } + } + argc -= optind; + argv += optind; + + /* No need to readlock since we aren't doing anything to the + repository. */ + err = start_recursion + ( unedit_fileproc, (FILESDONEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, + argc, argv, local, W_LOCAL, 0, 0, (char *) NULL, + 0, (char *) NULL ); + + err += send_notifications (argc, argv, local); + + return err; +} + + + +void +mark_up_to_date (const char *file) +{ + char *base; + + /* The file is up to date, so we better get rid of an out of + date file in CVSADM_BASE. */ + base = xmalloc (strlen (file) + 80); + strcpy (base, CVSADM_BASE); + strcat (base, "/"); + strcat (base, file); + if (unlink_file (base) < 0 && ! existence_error (errno)) + error (0, errno, "cannot remove %s", file); + free (base); +} + + + +void +editor_set (const char *filename, const char *editor, const char *val) +{ + char *edlist; + char *newlist; + + edlist = fileattr_get0 (filename, "_editors"); + newlist = fileattr_modify (edlist, editor, val, '>', ','); + /* If the attributes is unchanged, don't rewrite the attribute file. */ + if (!((edlist == NULL && newlist == NULL) + || (edlist != NULL + && newlist != NULL + && strcmp (edlist, newlist) == 0))) + fileattr_set (filename, "_editors", newlist); + if (edlist != NULL) + free (edlist); + if (newlist != NULL) + free (newlist); +} + +struct notify_proc_args { + /* What kind of notification, "edit", "tedit", etc. */ + const char *type; + /* User who is running the command which causes notification. */ + const char *who; + /* User to be notified. */ + const char *notifyee; + /* File. */ + const char *file; +}; + + + +static int +notify_proc (const char *repository, const char *filter, void *closure) +{ + char *cmdline; + FILE *pipefp; + const char *srepos = Short_Repository (repository); + struct notify_proc_args *args = (struct notify_proc_args *)closure; + + cmdline = format_cmdline ( +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + 0, srepos, +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + filter, + "p", "s", srepos, + "r", "s", current_parsed_root->directory, + "s", "s", args->notifyee, + NULL + ); + if (!cmdline || !strlen(cmdline)) + { + if (cmdline) free (cmdline); + error(0, 0, "pretag proc resolved to the empty string!"); + return (1); + } + + pipefp = run_popen (cmdline, "w"); + if (pipefp == NULL) + { + error (0, errno, "cannot write entry to notify filter: %s", cmdline); + free(cmdline); + return 1; + } + + fprintf (pipefp, "%s %s\n---\n", srepos, args->file); + fprintf (pipefp, "Triggered %s watch on %s\n", args->type, repository); + fprintf (pipefp, "By %s\n", args->who); + + /* Lots more potentially useful information we could add here; see + logfile_write for inspiration. */ + + free(cmdline); + return (pclose (pipefp)); +} + + + +/* FIXME: this function should have a way to report whether there was + an error so that server.c can know whether to report Notified back + to the client. */ +void +notify_do (int type, const char *filename, const char *who, const char *val, + const char *watches, const char *repository) +{ + static struct addremove_args blank; + struct addremove_args args; + char *watchers; + char *p; + char *endp; + char *nextp; + + /* Initialize fields to 0, NULL, or 0.0. */ + args = blank; + switch (type) + { + case 'E': + if (strpbrk (val, ",>;=\n") != NULL) + { + error (0, 0, "invalid character in editor value"); + return; + } + editor_set (filename, who, val); + break; + case 'U': + case 'C': + editor_set (filename, who, NULL); + break; + default: + return; + } + + watchers = fileattr_get0 (filename, "_watchers"); + p = watchers; + while (p != NULL) + { + char *q; + char *endq; + char *nextq; + char *notif; + + endp = strchr (p, '>'); + if (endp == NULL) + break; + nextp = strchr (p, ','); + + if ((size_t)(endp - p) == strlen (who) && strncmp (who, p, endp - p) == 0) + { + /* Don't notify user of their own changes. Would perhaps + be better to check whether it is the same working + directory, not the same user, but that is hairy. */ + p = nextp == NULL ? nextp : nextp + 1; + continue; + } + + /* Now we point q at a string which looks like + "edit+unedit+commit,"... and walk down it. */ + q = endp + 1; + notif = NULL; + while (q != NULL) + { + endq = strchr (q, '+'); + if (endq == NULL || (nextp != NULL && endq > nextp)) + { + if (nextp == NULL) + endq = q + strlen (q); + else + endq = nextp; + nextq = NULL; + } + else + nextq = endq + 1; + + /* If there is a temporary and a regular watch, send a single + notification, for the regular watch. */ + if (type == 'E' && endq - q == 4 && strncmp ("edit", q, 4) == 0) + { + notif = "edit"; + } + else if (type == 'U' + && endq - q == 6 && strncmp ("unedit", q, 6) == 0) + { + notif = "unedit"; + } + else if (type == 'C' + && endq - q == 6 && strncmp ("commit", q, 6) == 0) + { + notif = "commit"; + } + else if (type == 'E' + && endq - q == 5 && strncmp ("tedit", q, 5) == 0) + { + if (notif == NULL) + notif = "temporary edit"; + } + else if (type == 'U' + && endq - q == 7 && strncmp ("tunedit", q, 7) == 0) + { + if (notif == NULL) + notif = "temporary unedit"; + } + else if (type == 'C' + && endq - q == 7 && strncmp ("tcommit", q, 7) == 0) + { + if (notif == NULL) + notif = "temporary commit"; + } + q = nextq; + } + if (nextp != NULL) + ++nextp; + + if (notif != NULL) + { + struct notify_proc_args args; + size_t len = endp - p; + FILE *fp; + char *usersname; + char *line = NULL; + size_t line_len = 0; + + args.notifyee = NULL; + usersname = xmalloc (strlen (current_parsed_root->directory) + + sizeof CVSROOTADM + + sizeof CVSROOTADM_USERS + + 20); + strcpy (usersname, current_parsed_root->directory); + strcat (usersname, "/"); + strcat (usersname, CVSROOTADM); + strcat (usersname, "/"); + strcat (usersname, CVSROOTADM_USERS); + fp = CVS_FOPEN (usersname, "r"); + if (fp == NULL && !existence_error (errno)) + error (0, errno, "cannot read %s", usersname); + if (fp != NULL) + { + while (getline (&line, &line_len, fp) >= 0) + { + if (strncmp (line, p, len) == 0 + && line[len] == ':') + { + char *cp; + args.notifyee = xstrdup (line + len + 1); + + /* There may or may not be more + colon-separated fields added to this in the + future; in any case, we ignore them right + now, and if there are none we make sure to + chop off the final newline, if any. */ + cp = strpbrk (args.notifyee, ":\n"); + + if (cp != NULL) + *cp = '\0'; + break; + } + } + if (ferror (fp)) + error (0, errno, "cannot read %s", usersname); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", usersname); + } + free (usersname); + if (line != NULL) + free (line); + + if (args.notifyee == NULL) + { + char *tmp; + tmp = xmalloc (endp - p + 1); + strncpy (tmp, p, endp - p); + tmp[endp - p] = '\0'; + args.notifyee = tmp; + } + + args.type = notif; + args.who = who; + args.file = filename; + + (void) Parse_Info (CVSROOTADM_NOTIFY, repository, notify_proc, + PIOPT_ALL, &args); + /* It's okay to cast out the const for the free() below since we + * just allocated this a few lines above. The const was for + * everybody else. + */ + free ((char *)args.notifyee); + } + + p = nextp; + } + if (watchers != NULL) + free (watchers); + + switch (type) + { + case 'E': + if (*watches == 'E') + { + args.add_tedit = 1; + ++watches; + } + if (*watches == 'U') + { + args.add_tunedit = 1; + ++watches; + } + if (*watches == 'C') + { + args.add_tcommit = 1; + } + watch_modify_watchers (filename, &args); + break; + case 'U': + case 'C': + args.remove_temp = 1; + watch_modify_watchers (filename, &args); + break; + } +} + + + +#ifdef CLIENT_SUPPORT +/* Check and send notifications. This is only for the client. */ +void +notify_check (const char *repository, const char *update_dir) +{ + FILE *fp; + char *line = NULL; + size_t line_len = 0; + + if (! server_started) + /* We are in the midst of a command which is not to talk to + the server (e.g. the first phase of a cvs edit). Just chill + out, we'll catch the notifications on the flip side. */ + return; + + /* We send notifications even if noexec. I'm not sure which behavior + is most sensible. */ + + fp = CVS_FOPEN (CVSADM_NOTIFY, "r"); + if (fp == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot open %s", CVSADM_NOTIFY); + return; + } + while (getline (&line, &line_len, fp) > 0) + { + int notif_type; + char *filename; + char *val; + char *cp; + + notif_type = line[0]; + if (notif_type == '\0') + continue; + filename = line + 1; + cp = strchr (filename, '\t'); + if (cp == NULL) + continue; + *cp++ = '\0'; + val = cp; + + 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) + error (0, errno, "cannot close %s", CVSADM_NOTIFY); + + /* Leave the CVSADM_NOTIFY file there, until the server tells us it + has dealt with it. */ +} +#endif /* CLIENT_SUPPORT */ + + +static const char *const editors_usage[] = +{ + "Usage: %s %s [-lR] [files...]\n", + "\t-l\tProcess this directory only (not recursive).\n", + "\t-R\tProcess directories recursively.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +static int editors_fileproc (void *callerdat, struct file_info *finfo); + +static int +editors_fileproc (void *callerdat, struct file_info *finfo) +{ + char *them; + char *p; + + them = fileattr_get0 (finfo->file, "_editors"); + if (them == NULL) + return 0; + + cvs_output (finfo->fullname, 0); + + p = them; + while (1) + { + cvs_output ("\t", 1); + while (*p != '>' && *p != '\0') + cvs_output (p++, 1); + if (*p == '\0') + { + /* Only happens if attribute is misformed. */ + cvs_output ("\n", 1); + break; + } + ++p; + cvs_output ("\t", 1); + while (1) + { + while (*p != '+' && *p != ',' && *p != '\0') + cvs_output (p++, 1); + if (*p == '\0') + { + cvs_output ("\n", 1); + goto out; + } + if (*p == ',') + { + ++p; + break; + } + ++p; + cvs_output ("\t", 1); + } + cvs_output ("\n", 1); + } + out:; + free (them); + return 0; +} + +int +editors (int argc, char **argv) +{ + int local = 0; + int c; + + if (argc == -1) + usage (editors_usage); + + optind = 0; + while ((c = getopt (argc, argv, "+lR")) != -1) + { + switch (c) + { + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case '?': + default: + usage (editors_usage); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + start_server (); + ign_setup (); + + if (local) + send_arg ("-l"); + send_arg ("--"); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + 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, NULL, + argc, argv, local, W_LOCAL, 0, 1, (char *) NULL, + 0, (char *) NULL ); +} diff --git a/contrib/cvs-1.12.9/src/edit.h b/contrib/cvs-1.12.9/src/edit.h new file mode 100644 index 0000000000..b341ab095f --- /dev/null +++ b/contrib/cvs-1.12.9/src/edit.h @@ -0,0 +1,40 @@ +/* Interface to "cvs edit", "cvs watch on", and related features + + 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. */ + +extern int watch_on (int argc, char **argv); +extern int watch_off (int argc, char **argv); + +#ifdef CLIENT_SUPPORT +/* Check to see if any notifications are sitting around in need of being + sent. These are the notifications stored in CVSADM_NOTIFY (edit,unedit); + commit calls notify_do directly. */ +extern void notify_check (const char *repository, const char *update_dir); +#endif /* CLIENT_SUPPORT */ + +/* Issue a notification for file FILENAME. TYPE is 'E' for edit, 'U' + for unedit, and 'C' for commit. WHO is the user currently running. + For TYPE 'E', VAL is the time+host+directory data which goes in + _editors, and WATCHES is zero or more of E,U,C, in that order, to specify + what kinds of temporary watches to set. */ +extern void notify_do (int type, const char *filename, const char *who, + const char *val, const char *watches, + const char *repository); + +/* Set attributes to reflect the fact that EDITOR is editing FILENAME. + VAL is time+host+directory, or NULL if we are to say that EDITOR is + *not* editing FILENAME. */ +extern void editor_set (const char *filename, const char *editor, + const char *val); + +/* Take note of the fact that FILE is up to date (this munges CVS/Base; + processing of CVS/Entries is done separately). */ +extern void mark_up_to_date (const char *file); diff --git a/contrib/cvs-1.12.9/src/entries.c b/contrib/cvs-1.12.9/src/entries.c new file mode 100644 index 0000000000..7448f8ab38 --- /dev/null +++ b/contrib/cvs-1.12.9/src/entries.c @@ -0,0 +1,1154 @@ +/* + * 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 source distribution. + * + * Entries file to Files file + * + * Creates the file Files containing the names that comprise the project, from + * the Entries file. + */ + +#include "cvs.h" +#include "getline.h" + +static Node *AddEntryNode (List * list, Entnode *entnode); + +static Entnode *fgetentent (FILE *, char *, int *); +static int fputentent (FILE *, Entnode *); + +static Entnode *subdir_record (int, const char *, const char *); + +static FILE *entfile; +static char *entfilename; /* for error messages */ + + + +/* + * Construct an Entnode + */ +static Entnode * +Entnode_Create (enum ent_type type, const char *user, const char *vn, + const char *ts, const char *options, const char *tag, + const char *date, const char *ts_conflict) +{ + Entnode *ent; + + /* 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 : ""); + ent->options = xstrdup (options ? options : ""); + ent->tag = xstrdup (tag); + ent->date = xstrdup (date); + ent->conflict = xstrdup (ts_conflict); + + return ent; +} + +/* + * Destruct an Entnode + */ +static void Entnode_Destroy (Entnode *); + +static void +Entnode_Destroy (Entnode *ent) +{ + free (ent->user); + free (ent->version); + free (ent->timestamp); + free (ent->options); + if (ent->tag) + free (ent->tag); + if (ent->date) + free (ent->date); + if (ent->conflict) + free (ent->conflict); + free (ent); +} + +/* + * Write out the line associated with a node of an entries file + */ +static int write_ent_proc (Node *, void *); +static int +write_ent_proc (Node *node, void *closure) +{ + 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); +} + +/* + * write out the current entries file given a list, making a backup copy + * first of course + */ +static void +write_entries (List *list) +{ + int sawdir; + + sawdir = 0; + + /* open the new one and walk the list writing entries */ + entfilename = CVSADM_ENTBAK; + entfile = CVS_FOPEN (entfilename, "w+"); + if (entfile == NULL) + { + /* Make this a warning, not an error. For example, one user might + have checked out a working directory which, for whatever reason, + contains an Entries.Log file. A second user, without write access + to that working directory, might want to do a "cvs log". The + problem rewriting Entries shouldn't affect the ability of "cvs log" + to work, although the warning is probably a good idea so that + whether Entries gets rewritten is not an inexplicable process. */ + /* FIXME: should be including update_dir in message. */ + error (0, errno, "cannot rewrite %s", entfilename); + + /* Now just return. We leave the Entries.Log file around. As far + as I know, there is never any data lying around in 'list' that + is not in Entries.Log at this time (if there is an error writing + Entries.Log that is a separate problem). */ + return; + } + + (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 = 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); + + /* now, atomically (on systems that support it) rename it */ + rename_file (entfilename, CVSADM_ENT); + + /* now, remove the log file */ + if (unlink_file (CVSADM_ENTLOG) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", CVSADM_ENTLOG); +} + + + +/* + * Removes the argument file from the Entries file if necessary. + */ +void +Scratch_Entry (List *list, const char *fname) +{ + Node *node; + + TRACE ( 1, "Scratch_Entry(%s)", 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 + } +} + + + +/* + * Enters the given file name/version/time-stamp into the Entries file, + * removing the old entry first, if necessary. + */ +void +Register (List *list, const char *fname, const char *vn, const char *ts, + const char *options, const char *tag, const char *date, + const char *ts_conflict) +{ + Entnode *entnode; + Node *node; + +#ifdef SERVER_SUPPORT + if (server_active) + { + server_register (fname, vn, ts, options, tag, date, ts_conflict); + } +#endif + + TRACE ( 1, "Register(%s, %s, %s%s%s, %s, %s %s)", + fname, vn, ts ? ts : "", + ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "", + options, tag ? tag : "", date ? date : "" ); + + entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date, + ts_conflict); + node = AddEntryNode (list, entnode); + + if (!noexec) + { + entfilename = CVSADM_ENTLOG; + entfile = CVS_FOPEN (entfilename, "a"); + + if (entfile == NULL) + { + /* Warning, not error, as in write_entries. */ + /* FIXME-update-dir: should be including update_dir in message. */ + error (0, errno, "cannot open %s", entfilename); + return; + } + + 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", entfilename); + } +} + +/* + * Node delete procedure for list-private sticky dir tag/date info + */ +static void +freesdt (Node *p) +{ + struct stickydirtag *sdtp = p->data; + + if (sdtp->tag) + free (sdtp->tag); + if (sdtp->date) + free (sdtp->date); + free ((char *) sdtp); +} + +/* Return the next real Entries line. On end of file, returns NULL. + On error, prints an error message and returns NULL. */ + +static Entnode * +fgetentent(FILE *fpin, char *cmd, int *sawdir) +{ + Entnode *ent; + char *line; + size_t line_chars_allocated; + register char *cp; + enum ent_type type; + char *l, *user, *vn, *ts, *options; + char *tag_or_date, *tag, *date, *ts_conflict; + int line_length; + + line = NULL; + line_chars_allocated = 0; + + ent = NULL; + while ((line_length = getline (&line, &line_chars_allocated, fpin)) > 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 = l + 1; + if ((cp = strchr (user, '/')) == NULL) + continue; + *cp++ = '\0'; + vn = cp; + if ((cp = strchr (vn, '/')) == NULL) + continue; + *cp++ = '\0'; + ts = cp; + if ((cp = strchr (ts, '/')) == NULL) + continue; + *cp++ = '\0'; + options = cp; + if ((cp = strchr (options, '/')) == NULL) + continue; + *cp++ = '\0'; + tag_or_date = cp; + if ((cp = strchr (tag_or_date, '\n')) == NULL) + continue; + *cp = '\0'; + tag = (char *) NULL; + date = (char *) NULL; + if (*tag_or_date == 'T') + tag = tag_or_date + 1; + else if (*tag_or_date == 'D') + date = tag_or_date + 1; + + if ((ts_conflict = strchr (ts, '+'))) + *ts_conflict++ = '\0'; + + /* + * XXX - Convert timestamp from old format to new format. + * + * If the timestamp doesn't match the file's current + * mtime, we'd have to generate a string that doesn't + * match anyways, so cheat and base it on the existing + * string; it doesn't have to match the same mod time. + * + * For an unmodified file, write the correct timestamp. + */ + { + struct stat sb; + if (strlen (ts) > 30 && CVS_STAT (user, &sb) == 0) + { + char *c = ctime (&sb.st_mtime); + /* Fix non-standard format. */ + if (c[8] == '0') c[8] = ' '; + + if (!strncmp (ts + 25, c, 24)) + ts = time_stamp (user); + else + { + ts += 24; + ts[0] = '*'; + } + } + } + + ent = Entnode_Create (type, user, vn, ts, options, tag, date, + ts_conflict); + break; + } + + if (line_length < 0 && !feof (fpin)) + error (0, errno, "cannot read entries file"); + + free (line); + return ent; +} + +static int +fputentent(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) + { + if (fprintf (fp, "+%s", p->conflict) < 0) + return 1; + } + if (fprintf (fp, "/%s/", p->options) < 0) + return 1; + + if (p->tag) + { + if (fprintf (fp, "T%s\n", p->tag) < 0) + return 1; + } + else if (p->date) + { + if (fprintf (fp, "D%s\n", p->date) < 0) + return 1; + } + else + { + if (fprintf (fp, "\n") < 0) + return 1; + } + + return 0; +} + + +/* Read the entries file into a list, hashing on the file name. + + UPDATE_DIR is the name of the current directory, for use in error + messages, or NULL if not known (that is, noone has gotten around + to updating the caller to pass in the information). */ +List * +Entries_Open (int aflag, char *update_dir) +{ + 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 (); + + /* + * 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, &dirnonbranch); + if (aflag || dirtag || dirdate) + { + 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 = sdtp; + entries->list->delproc = freesdt; + } + + sawdir = 0; + + fpin = CVS_FOPEN (CVSADM_ENT, "r"); + if (fpin == NULL) + { + if (update_dir != NULL) + error (0, 0, "in directory %s:", update_dir); + error (0, errno, "cannot open %s for reading", CVSADM_ENT); + } + else + { + while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL) + { + (void) AddEntryNode (entries, ent); + } + + if (fclose (fpin) < 0) + /* FIXME-update-dir: should include update_dir in message. */ + error (0, errno, "cannot close %s", CVSADM_ENT); + } + + fpin = CVS_FOPEN (CVSADM_ENTLOG, "r"); + if (fpin != NULL) + { + char cmd; + Node *node; + + while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL) + { + 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; + if (fclose (fpin) < 0) + /* FIXME-update-dir: should include update_dir in message. */ + error (0, errno, "cannot close %s", CVSADM_ENTLOG); + } + + /* 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 = sdtp; + entries->list->delproc = freesdt; + } + + if (do_rewrite && !noexec) + write_entries (entries); + + /* clean up and return */ + if (dirtag) + free (dirtag); + if (dirdate) + free (dirdate); + return (entries); +} + +void +Entries_Close(List *list) +{ + if (list) + { + if (!noexec) + { + if (isfile (CVSADM_ENTLOG)) + write_entries (list); + } + dellist(&list); + } +} + + +/* + * Free up the memory associated with the data section of an ENTRIES type + * node + */ +static void +Entries_delproc (Node *node) +{ + Entnode *p = node->data; + + Entnode_Destroy(p); +} + +/* + * Get an Entries file list node, initialize it, and add it to the specified + * list + */ +static Node * +AddEntryNode (List *list, Entnode *entdata) +{ + Node *p; + + /* was it already there? */ + if ((p = findnode_fn (list, entdata->user)) != NULL) + { + /* take it out */ + delnode (p); + } + + /* get a node and fill in the regular stuff */ + p = getnode (); + p->type = ENTRIES; + p->delproc = Entries_delproc; + + /* this one gets a key of the name for hashing */ + /* FIXME This results in duplicated data --- the hash package shouldn't + assume that the key is dynamically allocated. The user's free proc + should be responsible for freeing the key. */ + p->key = xstrdup (entdata->user); + p->data = entdata; + + /* put the node into the list */ + addnode (list, p); + return (p); +} + + + +/* + * Write out the CVS/Template file. + */ +void +WriteTemplate (const char *update_dir, int xdotemplate, const char *repository) +{ +#ifdef SERVER_SUPPORT + TRACE (1, "Write_Template (%s, %s)", update_dir, repository); + + if (noexec) + return; + + if (server_active && xdotemplate) + { + /* Clear the CVS/Template if supported to allow for the case + * where the rcsinfo file no longer has an entry for this + * directory. + */ + server_clear_template (update_dir, repository); + server_template (update_dir, repository); + } +#endif + + return; +} + + + +/* + * Write out/Clear the CVS/Tag file. + */ +void +WriteTag (const char *dir, const char *tag, const char *date, int nonbranch, + const char *update_dir, const char *repository) +{ + FILE *fout; + char *tmp; + + if (noexec) + return; + + tmp = xmalloc ((dir ? strlen (dir) : 0) + + sizeof (CVSADM_TAG) + + 10); + if (dir == NULL) + (void) strcpy (tmp, CVSADM_TAG); + else + (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG); + + if (tag || date) + { + fout = open_file (tmp, "w+"); + if (tag) + { + if (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 + { + if (fprintf (fout, "D%s\n", date) < 0) + error (1, errno, "write to %s failed", tmp); + } + if (fclose (fout) == EOF) + error (1, errno, "cannot close %s", tmp); + } + 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. + + 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. + + If there is an error, print an error message, set *DATEP and *TAGP + to NULL, and return. */ +void +ParseTag (char **tagp, char **datep, int *nonbranchp) +{ + FILE *fp; + + if (tagp) + *tagp = (char *) NULL; + if (datep) + *datep = (char *) NULL; + /* 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; + int line_length; + size_t line_chars_allocated; + + 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'; + 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; + } + } + + if (line_length < 0) + { + /* FIXME-update-dir: should include update_dir in messages. */ + if (feof (fp)) + error (0, 0, "cannot read %s: end of file", CVSADM_TAG); + else + error (0, errno, "cannot read %s", CVSADM_TAG); + } + + if (fclose (fp) < 0) + /* FIXME-update-dir: should include update_dir in message. */ + error (0, errno, "cannot close %s", CVSADM_TAG); + + free (line); + } + else if (!existence_error (errno)) + /* FIXME-update-dir: should include update_dir in message. */ + error (0, errno, "cannot open %s", CVSADM_TAG); +} + +/* + * 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 (List *entries) +{ + struct stickydirtag *sdtp = entries->list->data; + + /* If there is no list private data, that means that the + subdirectory information is known. */ + if (sdtp != NULL && ! sdtp->subdirs) + { + FILE *fp; + + sdtp->subdirs = 1; + if (!noexec) + { + /* Create Entries.Log so that Entries_Close will do something. */ + entfilename = CVSADM_ENTLOG; + fp = CVS_FOPEN (entfilename, "a"); + if (fp == NULL) + { + int save_errno = errno; + + /* As in subdir_record, just silently skip the whole thing + if there is no CVSADM directory. */ + if (! isdir (CVSADM)) + return; + error (1, save_errno, "cannot open %s", entfilename); + } + else + { + if (fclose (fp) == EOF) + error (1, errno, "cannot close %s", entfilename); + } + } + } +} + +/* Record subdirectory information. */ + +static Entnode * +subdir_record (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 (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 (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); + } +} + + + +/* OK, the following base_* code tracks the revisions of the files in + CVS/Base. We do this in a file CVS/Baserev. Separate from + CVS/Entries because it needs to go in separate data structures + anyway (the name in Entries must be unique), so this seemed + cleaner. The business of rewriting the whole file in + base_deregister and base_register is the kind of thing we used to + do for Entries and which turned out to be slow, which is why there + is now the Entries.Log machinery. So maybe from that point of + view it is a mistake to do this separately from Entries, I dunno. + + We also need something analogous for: + + 1. CVS/Template (so we can update the Template file automagically + without the user needing to check out a new working directory). + Updating would probably print a message (that part might be + optional, although probably it should be visible because not all + cvs commands would make the update happen and so it is a + user-visible behavior). Constructing version number for template + is a bit hairy (base it on the timestamp on the server? Or see if + the template is in checkoutlist and if yes use its versioning and + if no don't version it?).... + + 2. cvsignore (need to keep a copy in the working directory to do + "cvs release" on the client side; see comment at src/release.c + (release). Would also allow us to stop needing Questionable. */ + +enum base_walk { + /* Set the revision for FILE to *REV. */ + BASE_REGISTER, + /* Get the revision for FILE and put it in a newly malloc'd string + in *REV, or put NULL if not mentioned. */ + BASE_GET, + /* Remove FILE. */ + BASE_DEREGISTER +}; + +static void base_walk (enum base_walk, struct file_info *, char **); + +/* Read through the lines in CVS/Baserev, taking the actions as documented + for CODE. */ + +static void +base_walk (enum base_walk code, struct file_info *finfo, char **rev) +{ + FILE *fp; + char *line; + size_t line_allocated; + FILE *newf; + char *baserev_fullname; + char *baserevtmp_fullname; + + line = NULL; + line_allocated = 0; + newf = NULL; + + /* First compute the fullnames for the error messages. This + computation probably should be broken out into a separate function, + as recurse.c does it too and places like Entries_Open should be + doing it. */ + baserev_fullname = xmalloc (sizeof (CVSADM_BASEREV) + + strlen (finfo->update_dir) + + 2); + baserev_fullname[0] = '\0'; + baserevtmp_fullname = xmalloc (sizeof (CVSADM_BASEREVTMP) + + strlen (finfo->update_dir) + + 2); + baserevtmp_fullname[0] = '\0'; + if (finfo->update_dir[0] != '\0') + { + strcat (baserev_fullname, finfo->update_dir); + strcat (baserev_fullname, "/"); + strcat (baserevtmp_fullname, finfo->update_dir); + strcat (baserevtmp_fullname, "/"); + } + strcat (baserev_fullname, CVSADM_BASEREV); + strcat (baserevtmp_fullname, CVSADM_BASEREVTMP); + + fp = CVS_FOPEN (CVSADM_BASEREV, "r"); + if (fp == NULL) + { + if (!existence_error (errno)) + { + error (0, errno, "cannot open %s for reading", baserev_fullname); + goto out; + } + } + + switch (code) + { + case BASE_REGISTER: + case BASE_DEREGISTER: + newf = CVS_FOPEN (CVSADM_BASEREVTMP, "w"); + if (newf == NULL) + { + error (0, errno, "cannot open %s for writing", + baserevtmp_fullname); + goto out; + } + break; + case BASE_GET: + *rev = NULL; + break; + } + + if (fp != NULL) + { + while (getline (&line, &line_allocated, fp) >= 0) + { + char *linefile; + char *p; + char *linerev; + + if (line[0] != 'B') + /* Ignore, for future expansion. */ + continue; + + linefile = line + 1; + p = strchr (linefile, '/'); + if (p == NULL) + /* Syntax error, ignore. */ + continue; + linerev = p + 1; + p = strchr (linerev, '/'); + if (p == NULL) + continue; + + linerev[-1] = '\0'; + if (fncmp (linefile, finfo->file) == 0) + { + switch (code) + { + case BASE_REGISTER: + case BASE_DEREGISTER: + /* Don't copy over the old entry, we don't want it. */ + break; + case BASE_GET: + *p = '\0'; + *rev = xstrdup (linerev); + *p = '/'; + goto got_it; + } + } + else + { + linerev[-1] = '/'; + switch (code) + { + case BASE_REGISTER: + case BASE_DEREGISTER: + if (fprintf (newf, "%s\n", line) < 0) + error (0, errno, "error writing %s", + baserevtmp_fullname); + break; + case BASE_GET: + break; + } + } + } + if (ferror (fp)) + error (0, errno, "cannot read %s", baserev_fullname); + } + got_it: + + if (code == BASE_REGISTER) + { + if (fprintf (newf, "B%s/%s/\n", finfo->file, *rev) < 0) + error (0, errno, "error writing %s", + baserevtmp_fullname); + } + + out: + + if (line != NULL) + free (line); + + if (fp != NULL) + { + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", baserev_fullname); + } + if (newf != NULL) + { + if (fclose (newf) < 0) + error (0, errno, "cannot close %s", baserevtmp_fullname); + rename_file (CVSADM_BASEREVTMP, CVSADM_BASEREV); + } + + free (baserev_fullname); + free (baserevtmp_fullname); +} + +/* Return, in a newly malloc'd string, the revision for FILE in CVS/Baserev, + or NULL if not listed. */ + +char * +base_get (struct file_info *finfo) +{ + char *rev; + base_walk (BASE_GET, finfo, &rev); + return rev; +} + +/* Set the revision for FILE to REV. */ + +void +base_register (struct file_info *finfo, char *rev) +{ + base_walk (BASE_REGISTER, finfo, &rev); +} + +/* Remove FILE. */ + +void +base_deregister (struct file_info *finfo) +{ + base_walk (BASE_DEREGISTER, finfo, NULL); +} diff --git a/contrib/cvs-1.12.9/src/error.c b/contrib/cvs-1.12.9/src/error.c new file mode 100644 index 0000000000..7d6bf534d5 --- /dev/null +++ b/contrib/cvs-1.12.9/src/error.c @@ -0,0 +1,178 @@ +/* error.c -- error handler for noninteractive utilities + Copyright (C) 1990-1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* David MacKenzie */ +/* Brian Berliner added support for CVS */ + +#include "cvs.h" + +/* If non-zero, error will use the CVS protocol to stdout 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 + process and packages up its stderr in the protocol. */ +int error_use_protocol; + +#ifndef strerror +extern char *strerror (int); +#endif + + + +/* Print the program name and error message MESSAGE, which is a printf-style + format string with optional args. This is a very limited printf subset: + %s, %d, %c, %x and %% only (without anything between the % and the s, + d, &c). Callers who want something fancier can use sprintf. + + If ERRNUM is nonzero, print its corresponding system error message. + Exit with status EXIT_FAILURE if STATUS is nonzero. If MESSAGE is "", + no need to print a message. + + I think this is largely cleaned up to the point where it does the right + thing for the server, whether the normal server_active (child process) + case or the error_use_protocol (parent process) case. The one exception + is that STATUS nonzero for error_use_protocol probably doesn't work yet; + in that case still need to use the pending_error machinery in server.c. + + error() does not molest errno; some code (e.g. Entries_Open) depends + on being able to say something like: + error (0, 0, "foo"); + error (0, errno, "bar"); + + */ + +/* VARARGS */ +void +error (int status, int errnum, const char *message, ...) +{ + int save_errno = errno; + + if (message[0] != '\0') + { + va_list args; + const char *p; + char *q; + char *str; + int num; + long lnum; + unsigned int unum; + unsigned long ulnum; + int ch; + char buf[100]; + + cvs_outerr (program_name, 0); + if (cvs_cmd_name && *cvs_cmd_name) + { + cvs_outerr (" ", 1); + if (status != 0) + cvs_outerr ("[", 1); + cvs_outerr (cvs_cmd_name, 0); + if (status != 0) + cvs_outerr (" aborted]", 0); + } + cvs_outerr (": ", 2); + + va_start( args, message ); + p = message; + while ((q = strchr (p, '%')) != NULL) + { + static const char msg[] = + "\ninternal error: bad % in error()\n"; + if (q - p > 0) + cvs_outerr (p, q - p); + + switch (q[1]) + { + case 's': + str = va_arg (args, char *); + cvs_outerr (str, strlen (str)); + break; + case 'd': + num = va_arg (args, int); + sprintf (buf, "%d", num); + cvs_outerr (buf, strlen (buf)); + break; + case 'l': + if (q[2] == 'd') + { + lnum = va_arg (args, long); + sprintf (buf, "%ld", lnum); + } + else if (q[2] == 'u') + { + ulnum = va_arg (args, unsigned long); + sprintf (buf, "%lu", ulnum); + } + else goto bad; + cvs_outerr (buf, strlen (buf)); + q++; + break; + case 'x': + unum = va_arg (args, unsigned int); + sprintf (buf, "%x", unum); + cvs_outerr (buf, strlen (buf)); + break; + case 'c': + ch = va_arg (args, int); + buf[0] = ch; + cvs_outerr (buf, 1); + break; + case '%': + cvs_outerr ("%", 1); + break; + default: + bad: + cvs_outerr (msg, sizeof (msg) - 1); + /* Don't just keep going, because q + 1 might point to the + terminating '\0'. */ + goto out; + } + p = q + 2; + } + cvs_outerr (p, strlen (p)); + out: + va_end (args); + + if (errnum != 0) + { + cvs_outerr (": ", 2); + cvs_outerr (strerror (errnum), 0); + } + cvs_outerr ("\n", 1); + } + + if (status) + exit (EXIT_FAILURE); + errno = save_errno; +} + +/* Print the program name and error message MESSAGE, which is a printf-style + format string with optional args to the file specified by FP. + If ERRNUM is nonzero, print its corresponding system error message. + Exit with status EXIT_FAILURE if STATUS is nonzero. */ +/* VARARGS */ +void +fperrmsg (FILE *fp, int status, int errnum, char *message, ...) +{ + va_list args; + + fprintf (fp, "%s: ", program_name); + va_start( args, message ); + vfprintf (fp, message, args); + va_end (args); + if (errnum) + fprintf (fp, ": %s", strerror (errnum)); + putc ('\n', fp); + fflush (fp); + if (status) + exit (EXIT_FAILURE); +} diff --git a/contrib/cvs-1.12.9/src/exithandle.c b/contrib/cvs-1.12.9/src/exithandle.c new file mode 100644 index 0000000000..a4fec1c0ee --- /dev/null +++ b/contrib/cvs-1.12.9/src/exithandle.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2003, Derek Price and Ximbiot + * + * You may distribute under the terms of the GNU General Public License + * as specified in the README file that comes with the CVS source + * distribution. + * + * This is a convenience wrapper for some of the functions in lib/sighandle.c. + */ + +#include "cvs.h" + +/* + * Register a handler for all signals. + */ +void +signals_register (RETSIGTYPE (*handler)(int)) +{ +#ifndef DONT_USE_SIGNALS +#ifdef SIGABRT + (void) SIG_register (SIGABRT, handler); +#endif +#ifdef SIGHUP + (void) SIG_register (SIGHUP, handler); +#endif +#ifdef SIGINT + (void) SIG_register (SIGINT, handler); +#endif +#ifdef SIGQUIT + (void) SIG_register (SIGQUIT, handler); +#endif +#ifdef SIGPIPE + (void) SIG_register (SIGPIPE, handler); +#endif +#ifdef SIGTERM + (void) SIG_register (SIGTERM, handler); +#endif +#endif /* !DONT_USE_SIGNALS */ +} + +/* + * Register a handler for all signals and exit. + */ +void +cleanup_register (void (*handler) (void)) +{ + signals_register (handler); + atexit (handler); +} diff --git a/contrib/cvs-1.12.9/src/expand_path.c b/contrib/cvs-1.12.9/src/expand_path.c new file mode 100644 index 0000000000..22baa1ca8e --- /dev/null +++ b/contrib/cvs-1.12.9/src/expand_path.c @@ -0,0 +1,393 @@ +/* expand_path.c -- expand environmental variables in passed in string + * + * The main routine is expand_path(), it is the routine that handles + * the '~' character in four forms: + * ~name + * ~name/ + * ~/ + * ~ + * and handles environment variables contained within the pathname + * which are defined by: + * ${var_name} (var_name is the name of the environ variable) + * $var_name (var_name ends w/ non-alphanumeric char other than '_') + */ + +#include "cvs.h" +#include + +static char *expand_variable (const char *env, const char *file, int line); + +/* User variables. */ + +List *variable_list = NULL; + +static void variable_delproc (Node *); + +static void +variable_delproc (Node *node) +{ + free (node->data); +} + +/* Currently used by -s option; we might want a way to set user + variables in a file in the $CVSROOT/CVSROOT directory too. */ + +void +variable_set (char *nameval) +{ + char *p; + char *name; + Node *node; + + p = nameval; + while (isalnum ((unsigned char) *p) || *p == '_') + ++p; + if (*p != '=') + error ( 1, 0, "invalid character in user variable name in %s", + nameval ); + if (p == nameval) + error (1, 0, "empty user variable name in %s", nameval); + name = xmalloc (p - nameval + 1); + strncpy (name, nameval, p - nameval); + name[p - nameval] = '\0'; + /* Make p point to the value. */ + ++p; + if (strchr (p, '\012') != NULL) + error (1, 0, "linefeed in user variable value in %s", nameval); + + if (variable_list == NULL) + variable_list = getlist (); + + node = findnode (variable_list, name); + if (node == NULL) + { + node = getnode (); + node->type = VARIABLE; + node->delproc = variable_delproc; + node->key = name; + node->data = xstrdup (p); + (void) addnode (variable_list, node); + } + else + { + /* Replace the old value. For example, this means that -s + options on the command line override ones from .cvsrc. */ + free (node->data); + node->data = xstrdup (p); + free (name); + } +} + + + +/* This routine will expand the pathname to account for ~ and $ + 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 (const char *name, const char *file, int line, int formatsafe) +{ + const char *s; + char *d; + + char *mybuf = NULL; + size_t mybuf_size = 0; + char *buf = NULL; + size_t buf_size = 0; + + size_t doff; + char inquotes = '\0'; + + 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 $ as we go. */ + s = name; + d = mybuf; + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; + while ((*d++ = *s) != '\0') + { + if (*s == '\\') + { + /* The next character is a literal. Leave the \ in the string since + * it will be needed again when the string is split into arguments. + */ + /* if we have a \ as the last character of the string, just leave it there + * - this is where we would set the escape flag to tell our parent we want + * another line if we cared. + */ + if (*++s) + { + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; + *d++ = *s++; + } + } + /* skip $ variable processing for text inside single quotes */ + else if (inquotes == '\'') + { + if (*s++ == '\'') + { + inquotes = '\0'; + } + } + else if (*s == '\'') + { + s++; + inquotes = '\''; + } + else if (*s == '"') + { + s++; + if (inquotes) inquotes = '\0'; + else inquotes = '"'; + } + else 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 ((unsigned char) *s) == 0 && *s != '_') + break; + 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; + if (d[-1] == '"') + { + /* escape the double quotes if we're between a matched pair of + * double quotes so that this sub will be passed inside as or as + * part of a single argument during the argument split later. + */ + if (inquotes) + { + d[-1] = '\\'; + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; + *d++ = '"'; + } + } + else if (formatsafe && d[-1] == '%') + { + /* escape '%' to get past printf style format strings later + * (in make_cmdline). + */ + doff = d - mybuf; + expand_string (&mybuf, &mybuf_size, doff + 1); + d = mybuf + doff; + *d++ = d[-1]; + } + } + --d; + if (flag && *s) + s++; + } + else + /* expand_variable has already printed an error message. */ + goto error_exit; + } + 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 + * This entire if statement including the else portion + */ + if (*s++ == '~') + { + char *t; + char *p, *pstart; + pstart = p = xstrdup (s); + if (*pstart=='/' || *pstart==0) + t = get_homedir (); + else + { +#ifdef GETPWNAM_MISSING + for (; *p!='/' && *p; p++) + ; + *p = 0; + if (line != 0) + error (0, 0, + "%s:%d:tilde expansion not supported on this system", + file, line); + else + error (0, 0, "%s:tilde expansion not supported on this system", + file); + return NULL; +#else + struct passwd *ps; + for (; *p!='/' && *p; p++) + ; + *p = 0; + ps = getpwnam (pstart); + if (ps == 0) + { + if (line != 0) + error (0, 0, "%s:%d: no such user %s", + file, line, pstart); + else + error (0, 0, "%s: no such user %s", file, pstart); + return NULL; + } + t = ps->pw_dir; +#endif + } + if (t == NULL) + error (1, 0, "cannot find home directory"); + + 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; + s+=p-pstart; + free (pstart); + } + else + --s; + /* Kill up to here */ + doff = d - buf; + expand_string (&buf, &buf_size, doff + 1); + d = buf + doff; + while ((*d++ = *s++)) + { + 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 * +expand_variable (const char *name, const char *file, int line) +{ + if (strcmp (name, CVSROOT_ENV) == 0) + return current_parsed_root->directory; + else if (strcmp (name, "RCSBIN") == 0) + { + error (0, 0, "RCSBIN internal variable is no longer supported"); + return NULL; + } + else if (strcmp (name, EDITOR1_ENV) == 0) + return Editor; + else if (strcmp (name, EDITOR2_ENV) == 0) + return Editor; + else if (strcmp (name, EDITOR3_ENV) == 0) + return Editor; + else if (strcmp (name, "USER") == 0) + return getcaller (); + else if (isalpha ((unsigned char) name[0])) + { + /* These names are reserved for future versions of CVS, + so that is why it is an error. */ + if (line != 0) + error (0, 0, "%s:%d: no such internal variable $%s", + file, line, name); + else + error (0, 0, "%s: no such internal variable $%s", + file, name); + return NULL; + } + else if (name[0] == '=') + { + Node *node; + /* Crazy syntax for a user variable. But we want + *something* that lets the user name a user variable + anything he wants, without interference from + (existing or future) internal variables. */ + node = findnode (variable_list, name + 1); + if (node == NULL) + { + if (line != 0) + error (0, 0, "%s:%d: no such user variable ${%s}", + file, line, name); + else + error (0, 0, "%s: no such user variable ${%s}", + file, name); + return NULL; + } + return node->data; + } + else + { + /* It is an unrecognized character. We return an error to + reserve these for future versions of CVS; it is plausible + that various crazy syntaxes might be invented for inserting + information about revisions, branches, etc. */ + if (line != 0) + error (0, 0, "%s:%d: unrecognized variable syntax %s", + file, line, name); + else + error (0, 0, "%s: unrecognized variable syntax %s", + file, name); + return NULL; + } +} diff --git a/contrib/cvs-1.12.9/src/fileattr.c b/contrib/cvs-1.12.9/src/fileattr.c new file mode 100644 index 0000000000..0a4b3026dd --- /dev/null +++ b/contrib/cvs-1.12.9/src/fileattr.c @@ -0,0 +1,636 @@ +/* Implementation for file attribute munging features. + + 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" +#include "fileattr.h" + +static void fileattr_read (void); +static int writeattr_proc (Node *, void *); + +/* Where to look for CVSREP_FILEATTR. */ +static char *fileattr_stored_repos; + +/* The in-memory attributes. */ +static List *attrlist; +static char *fileattr_default_attrs; +/* We have already tried to read attributes and failed in this directory + (for example, there is no CVSREP_FILEATTR file). */ +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 +fileattr_startdir (const char *repos) +{ + assert (fileattr_stored_repos == NULL); + fileattr_stored_repos = xstrdup (repos); + assert (attrlist == NULL); + attr_read_attempted = 0; + assert (unrecog_head == NULL); +} + + + +static void +fileattr_delproc (Node *node) +{ + assert (node->data != NULL); + free (node->data); + node->data = NULL; +} + +/* Read all the attributes for the current directory into memory. */ +static void +fileattr_read (void) +{ + char *fname; + FILE *fp; + char *line = NULL; + size_t line_len = 0; + + /* If there are no attributes, don't waste time repeatedly looking + for the CVSREP_FILEATTR file. */ + if (attr_read_attempted) + return; + + /* If NULL was passed to fileattr_startdir, then it isn't kosher to look + at attributes. */ + assert (fileattr_stored_repos != NULL); + + fname = xmalloc (strlen (fileattr_stored_repos) + + 1 + + sizeof (CVSREP_FILEATTR) + + 1); + + strcpy (fname, fileattr_stored_repos); + strcat (fname, "/"); + strcat (fname, CVSREP_FILEATTR); + + attr_read_attempted = 1; + fp = CVS_FOPEN (fname, FOPEN_BINARY_READ); + if (fp == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot read %s", fname); + free (fname); + return; + } + attrlist = getlist (); + while (1) { + int nread; + nread = getline (&line, &line_len, fp); + if (nread < 0) + break; + /* Remove trailing newline. */ + line[nread - 1] = '\0'; + if (line[0] == 'F') + { + char *p; + Node *newnode; + + p = strchr (line, '\t'); + if (p == NULL) + error (1, 0, + "file attribute database corruption: tab missing in %s", + fname); + *p++ = '\0'; + newnode = getnode (); + newnode->type = FILEATTR; + newnode->delproc = fileattr_delproc; + newnode->key = xstrdup (line + 1); + newnode->data = xstrdup (p); + 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. */ + freenode (newnode); + } + else if (line[0] == 'D') + { + char *p; + /* Currently nothing to skip here, but for future expansion, + ignore anything located here. */ + p = strchr (line, '\t'); + if (p == NULL) + error (1, 0, + "file attribute database corruption: tab missing in %s", + fname); + ++p; + fileattr_default_attrs = xstrdup (p); + } + 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); + if (line != NULL) + free (line); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", fname); + attrs_modified = 0; + free (fname); +} + +char * +fileattr_get (const char *filename, const char *attrname) +{ + Node *node; + size_t attrname_len = strlen (attrname); + char *p; + + if (attrlist == NULL) + fileattr_read (); + if (attrlist == NULL) + /* Either nothing has any attributes, or fileattr_read already printed + an error message. */ + return NULL; + + if (filename == NULL) + p = fileattr_default_attrs; + else + { + node = findnode (attrlist, filename); + if (node == NULL) + /* A file not mentioned has no attributes. */ + return NULL; + p = node->data; + } + while (p) + { + if (strncmp (attrname, p, attrname_len) == 0 + && p[attrname_len] == '=') + { + /* Found it. */ + return p + attrname_len + 1; + } + p = strchr (p, ';'); + if (p == NULL) + break; + ++p; + } + /* The file doesn't have this attribute. */ + return NULL; +} + +char * +fileattr_get0 (const char *filename, const char *attrname) +{ + char *cp; + char *cpend; + char *retval; + + cp = fileattr_get (filename, attrname); + if (cp == NULL) + return NULL; + cpend = strchr (cp, ';'); + if (cpend == NULL) + cpend = cp + strlen (cp); + retval = xmalloc (cpend - cp + 1); + strncpy (retval, cp, cpend - cp); + retval[cpend - cp] = '\0'; + return retval; +} + +char * +fileattr_modify (char *list, const char *attrname, const char *attrval, int namevalsep, int entsep) +{ + char *retval; + char *rp; + size_t attrname_len = strlen (attrname); + + /* Portion of list before the attribute to be replaced. */ + char *pre; + char *preend; + /* Portion of list after the attribute to be replaced. */ + char *post; + + char *p; + char *p2; + + p = list; + pre = list; + preend = NULL; + /* post is NULL unless set otherwise. */ + post = NULL; + p2 = NULL; + if (list != NULL) + { + while (1) { + p2 = strchr (p, entsep); + if (p2 == NULL) + { + p2 = p + strlen (p); + if (preend == NULL) + preend = p2; + } + else + ++p2; + if (strncmp (attrname, p, attrname_len) == 0 + && p[attrname_len] == namevalsep) + { + /* Found it. */ + preend = p; + if (preend > list) + /* Don't include the preceding entsep. */ + --preend; + + post = p2; + } + if (p2[0] == '\0') + break; + p = p2; + } + } + if (post == NULL) + post = p2; + + if (preend == pre && attrval == NULL && post == p2) + return NULL; + + retval = xmalloc ((preend - pre) + + 1 + + (attrval == NULL ? 0 : (attrname_len + 1 + + strlen (attrval))) + + 1 + + (p2 - post) + + 1); + if (preend != pre) + { + strncpy (retval, pre, preend - pre); + rp = retval + (preend - pre); + if (attrval != NULL) + *rp++ = entsep; + *rp = '\0'; + } + else + retval[0] = '\0'; + if (attrval != NULL) + { + strcat (retval, attrname); + rp = retval + strlen (retval); + *rp++ = namevalsep; + strcpy (rp, attrval); + } + if (post != p2) + { + rp = retval + strlen (retval); + if (preend != pre || attrval != NULL) + *rp++ = entsep; + strncpy (rp, post, p2 - post); + rp += p2 - post; + *rp = '\0'; + } + return retval; +} + +void +fileattr_set (const char *filename, const char *attrname, const char *attrval) +{ + Node *node; + char *p; + + if (filename == NULL) + { + p = fileattr_modify (fileattr_default_attrs, attrname, attrval, + '=', ';'); + if (fileattr_default_attrs != NULL) + free (fileattr_default_attrs); + fileattr_default_attrs = p; + attrs_modified = 1; + return; + } + if (attrlist == NULL) + fileattr_read (); + if (attrlist == NULL) + { + /* Not sure this is a graceful way to handle things + in the case where fileattr_read was unable to read the file. */ + /* No attributes existed previously. */ + attrlist = getlist (); + } + + node = findnode (attrlist, filename); + if (node == NULL) + { + if (attrval == NULL) + /* Attempt to remove an attribute which wasn't there. */ + return; + + /* First attribute for this file. */ + node = getnode (); + node->type = FILEATTR; + node->delproc = fileattr_delproc; + node->key = xstrdup (filename); + node->data = xmalloc (strlen (attrname) + 1 + strlen (attrval) + 1); + strcpy (node->data, attrname); + strcat (node->data, "="); + strcat (node->data, attrval); + addnode (attrlist, node); + } + + p = fileattr_modify (node->data, attrname, attrval, '=', ';'); + if (p == NULL) + delnode (node); + else + { + free (node->data); + node->data = p; + } + + attrs_modified = 1; +} + +char * +fileattr_getall (const char *filename) +{ + Node *node; + char *p; + + if (attrlist == NULL) + fileattr_read (); + if (attrlist == NULL) + /* Either nothing has any attributes, or fileattr_read already printed + an error message. */ + return NULL; + + if (filename == NULL) + p = fileattr_default_attrs; + else + { + node = findnode (attrlist, filename); + if (node == NULL) + /* A file not mentioned has no attributes. */ + return NULL; + p = node->data; + } + return xstrdup (p); +} + +void +fileattr_setall (const char *filename, const char *attrs) +{ + Node *node; + + if (filename == NULL) + { + if (fileattr_default_attrs != NULL) + free (fileattr_default_attrs); + fileattr_default_attrs = xstrdup (attrs); + attrs_modified = 1; + return; + } + if (attrlist == NULL) + fileattr_read (); + if (attrlist == NULL) + { + /* Not sure this is a graceful way to handle things + in the case where fileattr_read was unable to read the file. */ + /* No attributes existed previously. */ + attrlist = getlist (); + } + + node = findnode (attrlist, filename); + if (node == NULL) + { + /* The file had no attributes. Add them if we have any to add. */ + if (attrs != NULL) + { + node = getnode (); + node->type = FILEATTR; + node->delproc = fileattr_delproc; + node->key = xstrdup (filename); + node->data = xstrdup (attrs); + addnode (attrlist, node); + } + } + else + { + if (attrs == NULL) + delnode (node); + else + { + free (node->data); + node->data = xstrdup (attrs); + } + } + + attrs_modified = 1; +} + +void +fileattr_newfile (const char *filename) +{ + Node *node; + + if (attrlist == NULL) + fileattr_read (); + + if (fileattr_default_attrs == NULL) + return; + + if (attrlist == NULL) + { + /* Not sure this is a graceful way to handle things + in the case where fileattr_read was unable to read the file. */ + /* No attributes existed previously. */ + attrlist = getlist (); + } + + node = getnode (); + node->type = FILEATTR; + node->delproc = fileattr_delproc; + node->key = xstrdup (filename); + node->data = xstrdup (fileattr_default_attrs); + addnode (attrlist, node); + attrs_modified = 1; +} + +static int +writeattr_proc (Node *node, void *data) +{ + FILE *fp = (FILE *)data; + fputs ("F", fp); + fputs (node->key, fp); + fputs ("\t", fp); + fputs (node->data, fp); + fputs ("\012", fp); + return 0; +} + +void +fileattr_write (void) +{ + FILE *fp; + char *fname; + mode_t omask; + struct unrecog *p; + + if (!attrs_modified) + return; + + if (noexec) + return; + + /* If NULL was passed to fileattr_startdir, then it isn't kosher to set + attributes. */ + assert (fileattr_stored_repos != NULL); + + fname = xmalloc (strlen (fileattr_stored_repos) + + 1 + + sizeof (CVSREP_FILEATTR) + + 1); + + strcpy (fname, fileattr_stored_repos); + strcat (fname, "/"); + strcat (fname, CVSREP_FILEATTR); + + if (list_isempty (attrlist) + && fileattr_default_attrs == NULL + && unrecog_head == NULL) + { + /* There are no attributes. */ + if (unlink_file (fname) < 0) + { + if (!existence_error (errno)) + { + error (0, errno, "cannot remove %s", fname); + } + } + + /* Now remove CVSREP directory, if empty. The main reason we bother + is that CVS 1.6 and earlier will choke if a CVSREP directory + exists, so provide the user a graceful way to remove it. */ + strcpy (fname, fileattr_stored_repos); + strcat (fname, "/"); + strcat (fname, CVSREP); + if (CVS_RMDIR (fname) < 0) + { + if (errno != ENOTEMPTY + + /* Don't know why we would be here if there is no CVSREP + directory, but it seemed to be happening anyway, so + check for it. */ + && !existence_error (errno)) + error (0, errno, "cannot remove %s", fname); + } + + free (fname); + return; + } + + omask = umask (cvsumask); + fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE); + if (fp == NULL) + { + if (existence_error (errno)) + { + /* Maybe the CVSREP directory doesn't exist. Try creating it. */ + char *repname; + + repname = xmalloc (strlen (fileattr_stored_repos) + + 1 + + sizeof (CVSREP) + + 1); + strcpy (repname, fileattr_stored_repos); + strcat (repname, "/"); + strcat (repname, CVSREP); + + if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST) + { + error (0, errno, "cannot make directory %s", repname); + (void) umask (omask); + free (repname); + return; + } + free (repname); + + fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE); + } + if (fp == NULL) + { + error (0, errno, "cannot write %s", fname); + (void) umask (omask); + return; + } + } + (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. */ + for (p = unrecog_head; p != NULL; p = p->next) + { + fputs (p->line, fp); + fputs ("\012", fp); + } + + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", fname); + attrs_modified = 0; + free (fname); +} + +void +fileattr_free (void) +{ + /* Note that attrs_modified will ordinarily be zero, but there are + a few cases in which fileattr_write will fail to zero it (if + noexec is set, or error conditions). This probably is the way + it should be. */ + dellist (&attrlist); + if (fileattr_stored_repos != NULL) + free (fileattr_stored_repos); + fileattr_stored_repos = NULL; + if (fileattr_default_attrs != NULL) + free (fileattr_default_attrs); + fileattr_default_attrs = NULL; + while (unrecog_head) + { + struct unrecog *p = unrecog_head; + unrecog_head = p->next; + free (p->line); + free (p); + } +} diff --git a/contrib/cvs-1.12.9/src/fileattr.h b/contrib/cvs-1.12.9/src/fileattr.h new file mode 100644 index 0000000000..4ce8233387 --- /dev/null +++ b/contrib/cvs-1.12.9/src/fileattr.h @@ -0,0 +1,136 @@ +/* Declarations for file attribute munging features. + + 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. */ + +#ifndef FILEATTR_H + +/* File containing per-file attributes. The format of this file is in + cvs.texinfo but here is a quick summary. The file contains a + series of entries: + + ENT-TYPE FILENAME ATTRNAME = ATTRVAL + {; ATTRNAME = ATTRVAL} + + ENT-TYPE is 'F' for a file. + + ENT-TYPE is 'D', and FILENAME empty, for default attributes. + + Other ENT-TYPE are reserved for future expansion. + + Note that the order of the line is not significant; CVS is free to + rearrange them at its convenience. + + FIXME: this implementation doesn't handle '\0' in any of the + fields. We are encouraged to fix this (by cvs.texinfo). + + By convention, ATTRNAME starting with '_' is for an attribute given + special meaning by CVS; other ATTRNAMEs are for user-defined attributes + (or will be, once we add commands to manipulate user-defined attributes). + + Builtin attributes: + + _watched: Present means the file is watched and should be checked out + read-only. + + _watchers: Users with watches for this file. Value is + WATCHER > TYPE { , WATCHER > TYPE } + where WATCHER is a username, and TYPE is edit,unedit,commit separated by + + (or nothing if none; there is no "none" or "all" keyword). + + _editors: Users editing this file. Value is + EDITOR > VAL { , EDITOR > VAL } + where EDITOR is a username, and VAL is TIME+HOSTNAME+PATHNAME, where + TIME is when the "cvs edit" command happened, + and HOSTNAME and PATHNAME are for the working directory. */ + +#define CVSREP_FILEATTR "CVS/fileattr" + +/* Prepare for a new directory with repository REPOS. If REPOS is NULL, + then prepare for a "non-directory"; the caller can call fileattr_write + and fileattr_free, but must not call fileattr_get or fileattr_set. */ +extern void fileattr_startdir (const char *repos); + +/* Get the attribute ATTRNAME for file FILENAME. The return value + points into memory managed by the fileattr_* routines, should not + be altered by the caller, and is only good until the next call to + fileattr_clear or fileattr_set. It points to the value, terminated + 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 (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 (const char *filename, + const char *attrname); + +/* This is just a string manipulation function; it does not manipulate + file attributes as such. + + LIST is in the format + + ATTRNAME NAMEVALSEP ATTRVAL {ENTSEP ATTRNAME NAMEVALSEP ATTRVAL} + + And we want to put in an attribute with name NAME and value VAL, + replacing the already-present attribute with name NAME if there is + one. Or if VAL is NULL remove attribute NAME. Return a new + malloc'd list; don't muck with the one passed in. If we are removing + the last attribute return NULL. LIST can be NULL to mean that we + started out without any attributes. + + Examples: + + fileattr_modify ("abc=def", "xxx", "val", '=', ';')) => "abc=def;xxx=val" + fileattr_modify ("abc=def", "abc", "val", '=', ';')) => "abc=val" + fileattr_modify ("abc=v1;def=v2", "abc", "val", '=', ';')) + => "abc=val;def=v2" + fileattr_modify ("abc=v1;def=v2", "def", "val", '=', ';')) + => "abc=v1;def=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 (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 (const char *filename, const char *attrname, + const char *attrval); + +/* Get all the attributes for file FILENAME. They are returned as malloc'd + data in an unspecified format which is guaranteed only to be good for + passing to fileattr_setall, or NULL if no attributes. If FILENAME is + NULL, get default attributes. */ +extern char *fileattr_getall (const char *filename); + +/* Set the attributes for file FILENAME to ATTRS, overwriting all previous + attributes for that file. ATTRS was obtained from a previous call to + fileattr_getall (malloc'd data or NULL). */ +extern void fileattr_setall (const char *filename, const char *attrs); + +/* Set the attributes for file FILENAME in whatever manner is appropriate + for a newly created file. */ +extern void fileattr_newfile (const char *filename); + +/* Write out all modified attributes. */ +extern void fileattr_write (void); + +/* Free all memory allocated by fileattr_*. */ +extern void fileattr_free (void); + +#define FILEATTR_H 1 +#endif /* fileattr.h */ diff --git a/contrib/cvs-1.12.9/src/filesubr.c b/contrib/cvs-1.12.9/src/filesubr.c new file mode 100644 index 0000000000..1924a72149 --- /dev/null +++ b/contrib/cvs-1.12.9/src/filesubr.c @@ -0,0 +1,971 @@ +/* filesubr.c --- subroutines for dealing with files + Jim Blandy + + 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. */ + +/* These functions were moved out of subr.c because they need different + definitions under operating systems (like, say, Windows NT) with different + file system semantics. */ + +#include "cvs.h" + +static int deep_remove_dir (const char *path); + +/* + * Copies "from" to "to". + */ +void +copy_file (const char *from, const char *to) +{ + struct stat sb; + struct utimbuf t; + int fdin, fdout; + + TRACE ( 1, "copy(%s,%s)", from, to ); + + if (noexec) + return; + + /* If the file to be copied is a link or a device, then just create + the new link or device appropriately. */ + if (islink (from)) + { + char *source = xreadlink (from); + symlink (source, to); + free (source); + return; + } + + if (isdevice (from)) + { +#if defined(HAVE_MKNOD) && defined(HAVE_STRUCT_STAT_ST_RDEV) + if( CVS_STAT( from, &sb ) < 0 ) + error (1, errno, "cannot stat %s", from); + mknod (to, sb.st_mode, sb.st_rdev); +#else + error (1, 0, "cannot copy device files on this system (%s)", from); +#endif + } + else + { + /* Not a link or a device... probably a regular file. */ + if ((fdin = open (from, O_RDONLY)) < 0) + error (1, errno, "cannot open %s for copying", from); + if (fstat (fdin, &sb) < 0) + error (1, errno, "cannot fstat %s", from); + if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) + error (1, errno, "cannot create %s for copying", to); + if (sb.st_size > 0) + { + char buf[BUFSIZ]; + int n; + + for (;;) + { + n = read (fdin, buf, sizeof(buf)); + if (n == -1) + { +#ifdef EINTR + if (errno == EINTR) + continue; +#endif + error (1, errno, "cannot read file %s for copying", from); + } + else if (n == 0) + break; + + if (write(fdout, buf, n) != n) { + error (1, errno, "cannot write file %s for copying", to); + } + } + +#ifdef HAVE_FSYNC + if (fsync (fdout)) + error (1, errno, "cannot fsync file %s after copying", to); +#endif + } + + if (close (fdin) < 0) + error (0, errno, "cannot close %s", from); + if (close (fdout) < 0) + error (1, errno, "cannot close %s", to); + } + + /* now, set the times for the copied file to match those of the original */ + memset ((char *) &t, 0, sizeof (t)); + t.actime = sb.st_atime; + t.modtime = sb.st_mtime; + (void) utime (to, &t); +} + +/* FIXME-krp: these functions would benefit from caching the char * & + stat buf. */ + +/* + * Returns non-zero if the argument file is a directory, or is a symbolic + * link which points to a directory. + */ +int +isdir (const char *file) +{ + struct stat sb; + + if( CVS_STAT( file, &sb ) < 0 ) + return (0); + return (S_ISDIR (sb.st_mode)); +} + +/* + * Returns non-zero if the argument file is a symbolic link. + */ +int +islink (const char *file) +{ +#ifdef S_ISLNK + struct stat sb; + + if (CVS_LSTAT (file, &sb) < 0) + return (0); + return (S_ISLNK (sb.st_mode)); +#else + return (0); +#endif +} + +/* + * Returns non-zero if the argument file is a block or + * character special device. + */ +int +isdevice (const char *file) +{ + struct stat sb; + + if (CVS_LSTAT (file, &sb) < 0) + return (0); +#ifdef S_ISBLK + if (S_ISBLK (sb.st_mode)) + return 1; +#endif +#ifdef S_ISCHR + if (S_ISCHR (sb.st_mode)) + return 1; +#endif + return 0; +} + +/* + * Returns non-zero if the argument file exists. + */ +int +isfile (const char *file) +{ + return isaccessible(file, F_OK); +} + +/* + * Returns non-zero if the argument file is readable. + */ +int +isreadable (const char *file) +{ + return isaccessible(file, R_OK); +} + +/* + * Returns non-zero if the argument file is writable. + */ +int +iswritable (const char *file) +{ + return isaccessible(file, W_OK); +} + +/* + * Returns non-zero if the argument file is accessable according to + * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid + * bits set. + */ +int +isaccessible (const char *file, const int mode) +{ +#ifdef SETXID_SUPPORT + struct stat sb; + int umask = 0; + int gmask = 0; + int omask = 0; + int uid, mask; + + if( CVS_STAT( file, &sb ) == -1 ) + return 0; + if (mode == F_OK) + return 1; + + uid = geteuid(); + if (uid == 0) /* superuser */ + { + if (!(mode & X_OK) || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) + return 1; + + errno = EACCES; + return 0; + } + + if (mode & R_OK) + { + umask |= S_IRUSR; + gmask |= S_IRGRP; + omask |= S_IROTH; + } + if (mode & W_OK) + { + umask |= S_IWUSR; + gmask |= S_IWGRP; + omask |= S_IWOTH; + } + if (mode & X_OK) + { + umask |= S_IXUSR; + gmask |= S_IXGRP; + omask |= S_IXOTH; + } + + mask = sb.st_uid == uid ? umask : sb.st_gid == getegid() ? gmask : omask; + if ((sb.st_mode & mask) == mask) + return 1; + errno = EACCES; + return 0; +#else + return access(file, mode) == 0; +#endif +} + +/* + * Open a file and die if it fails + */ +FILE * +open_file (const char *name, const char *mode) +{ + FILE *fp; + + if ((fp = fopen (name, mode)) == NULL) + error (1, errno, "cannot open %s", name); + return (fp); +} + +/* + * Make a directory and die if it fails + */ +void +make_directory (const char *name) +{ + struct stat sb; + + if( CVS_STAT( name, &sb ) == 0 && ( !S_ISDIR( sb.st_mode ) ) ) + error (0, 0, "%s already exists but is not a directory", name); + if (!noexec && mkdir (name, 0777) < 0) + error (1, errno, "cannot make directory %s", name); +} + +/* + * Make a path to the argument directory, printing a message if something + * goes wrong. + */ +void +make_directories (const char *name) +{ + char *cp; + + if (noexec) + return; + + if (mkdir (name, 0777) == 0 || errno == EEXIST) + return; + if (! existence_error (errno)) + { + error (0, errno, "cannot make path to %s", name); + return; + } + if ((cp = strrchr (name, '/')) == NULL) + return; + *cp = '\0'; + make_directories (name); + *cp++ = '/'; + if (*cp == '\0') + return; + (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 (const char *name) +{ + if (mkdir (name, 0777) < 0) + { + int save_errno = errno; + if (save_errno != EEXIST && !isdir (name)) + error (1, save_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. + * + * Don't do anything if PreservePermissions is set to `yes'. This may + * have unexpected consequences for some uses of xchmod. + */ +void +xchmod (const char *fname, int writable) +{ + struct stat sb; + mode_t mode, oumask; + + if (preserve_perms) + return; + + if( CVS_STAT( fname, &sb ) < 0 ) + { + if (!noexec) + error (0, errno, "cannot stat %s", fname); + return; + } + oumask = umask (0); + (void) umask (oumask); + if (writable) + { + mode = sb.st_mode | (~oumask + & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) + | ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) + | ((sb.st_mode & S_IROTH) ? S_IWOTH : 0))); + } + else + { + mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask; + } + + TRACE ( 1, "chmod(%s,%o)", fname, (unsigned int) mode ); + + if (noexec) + return; + + if (chmod (fname, mode) < 0) + error (0, errno, "cannot change mode of file %s", fname); +} + +/* + * Rename a file and die if it fails + */ +void +rename_file (const char *from, const char *to) +{ + TRACE ( 1, "rename(%s,%s)", from, to ); + + if (noexec) + return; + + if (rename (from, to) < 0) + error (1, errno, "cannot rename file %s to %s", from, to); +} + +/* + * unlink a file, if possible. + */ +int +unlink_file (const char *f) +{ + TRACE ( 1, "unlink_file(%s)", f ); + + if (noexec) + return (0); + + return (CVS_UNLINK (f)); +} + + + +/* + * Unlink a file or dir, if possible. If it is a directory do a deep + * removal of all of the files in the directory. Return -1 on error + * (in which case errno is set). + */ +int +unlink_file_dir (const char *f) +{ + struct stat sb; + +#ifdef SERVER_SUPPORT + /* This is called by the server parent process in contexts where + it is not OK to send output (e.g. after we sent "ok" to the + client). */ + if (!server_active) +#endif + TRACE (TRACE_FUNCTION, "unlink_file_dir(%s)", f); + + if (noexec) + return 0; + + /* For at least some unices, if root tries to unlink() a directory, + instead of doing something rational like returning EISDIR, + the system will gleefully go ahead and corrupt the filesystem. + So we first call stat() to see if it is OK to call unlink(). This + doesn't quite work--if someone creates a directory between the + call to stat() and the call to unlink(), we'll still corrupt + the filesystem. Where is the Unix Haters Handbook when you need + it? */ + if( CVS_STAT( f, &sb ) < 0 ) + { + if (existence_error (errno)) + { + /* The file or directory doesn't exist anyhow. */ + return -1; + } + } + else if (S_ISDIR (sb.st_mode)) + return deep_remove_dir (f); + + return CVS_UNLINK (f); +} + + + +/* Remove a directory and everything it contains. Returns 0 for + * success, -1 for failure (in which case errno is set). + */ + +static int +deep_remove_dir (const char *path) +{ + DIR *dirp; + struct dirent *dp; + + if (rmdir (path) != 0) + { + 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 ((dirp = CVS_OPENDIR (path)) == NULL) + /* If unable to open the directory return + * an error + */ + return -1; + + errno = 0; + while ((dp = CVS_READDIR (dirp)) != NULL) + { + 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)) + { + if (deep_remove_dir (buf)) + { + CVS_CLOSEDIR (dirp); + free (buf); + return -1; + } + } + else + { + if (CVS_UNLINK (buf) != 0) + { + CVS_CLOSEDIR (dirp); + free (buf); + return -1; + } + } + free (buf); + + errno = 0; + } + if (errno != 0) + { + int save_errno = errno; + CVS_CLOSEDIR (dirp); + errno = save_errno; + return -1; + } + CVS_CLOSEDIR (dirp); + return rmdir (path); + } + else + return -1; + } + + /* Was able to remove the directory return 0 */ + return 0; +} + + + +/* Read NCHARS bytes from descriptor FD into BUF. + Return the number of characters successfully read. + The number returned is always NCHARS unless end-of-file or error. */ +static size_t +block_read (int fd, char *buf, size_t nchars) +{ + char *bp = buf; + size_t nread; + + do + { + nread = read (fd, bp, nchars); + if (nread == (size_t)-1) + { +#ifdef EINTR + if (errno == EINTR) + continue; +#endif + return (size_t)-1; + } + + if (nread == 0) + break; + + bp += nread; + nchars -= nread; + } while (nchars != 0); + + return bp - buf; +} + + +/* + * Compare "file1" to "file2". Return non-zero if they don't compare exactly. + * If FILE1 and FILE2 are special files, compare their salient characteristics + * (i.e. major/minor device numbers, links, etc. + */ +int +xcmp (const char *file1, const char *file2) +{ + char *buf1, *buf2; + struct stat sb1, sb2; + int fd1, fd2; + int ret; + + if (CVS_LSTAT (file1, &sb1) < 0) + error (1, errno, "cannot lstat %s", file1); + if (CVS_LSTAT (file2, &sb2) < 0) + error (1, errno, "cannot lstat %s", file2); + + /* If FILE1 and FILE2 are not the same file type, they are unequal. */ + if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT)) + return 1; + + /* If FILE1 and FILE2 are symlinks, they are equal if they point to + the same thing. */ +#ifdef S_ISLNK + if (S_ISLNK (sb1.st_mode) && S_ISLNK (sb2.st_mode)) + { + int result; + buf1 = xreadlink (file1); + buf2 = xreadlink (file2); + result = (strcmp (buf1, buf2) == 0); + free (buf1); + free (buf2); + return result; + } +#endif + + /* If FILE1 and FILE2 are devices, they are equal if their device + numbers match. */ + if (S_ISBLK (sb1.st_mode) || S_ISCHR (sb1.st_mode)) + { +#ifdef HAVE_STRUCT_STAT_ST_RDEV + if (sb1.st_rdev == sb2.st_rdev) + return 0; + else + return 1; +#else + error (1, 0, "cannot compare device files on this system (%s and %s)", + file1, file2); +#endif + } + + if ((fd1 = open (file1, O_RDONLY)) < 0) + error (1, errno, "cannot open file %s for comparing", file1); + if ((fd2 = open (file2, O_RDONLY)) < 0) + error (1, errno, "cannot open file %s for comparing", file2); + + /* A generic file compare routine might compare st_dev & st_ino here + to see if the two files being compared are actually the same file. + But that won't happen in CVS, so we won't bother. */ + + if (sb1.st_size != sb2.st_size) + ret = 1; + else if (sb1.st_size == 0) + ret = 0; + else + { + /* FIXME: compute the optimal buffer size by computing the least + common multiple of the files st_blocks field */ + size_t buf_size = 8 * 1024; + size_t read1; + size_t read2; + + buf1 = xmalloc (buf_size); + buf2 = xmalloc (buf_size); + + do + { + read1 = block_read (fd1, buf1, buf_size); + if (read1 == (size_t)-1) + error (1, errno, "cannot read file %s for comparing", file1); + + read2 = block_read (fd2, buf2, buf_size); + if (read2 == (size_t)-1) + error (1, errno, "cannot read file %s for comparing", file2); + + /* assert (read1 == read2); */ + + ret = memcmp(buf1, buf2, read1); + } while (ret == 0 && read1 == buf_size); + + free (buf1); + free (buf2); + } + + (void) close (fd1); + (void) close (fd2); + return (ret); +} + +/* Generate a unique temporary filename. Returns a pointer to a newly + * malloc'd string containing the name. Returns successfully or not at + * all. + * + * THIS FUNCTION IS DEPRECATED!!! USE cvs_temp_file INSTEAD!!! + * + * and yes, I know about the way the rcs commands use temp files. I think + * they should be converted too but I don't have time to look into it right + * now. + */ +char * +cvs_temp_name (void) +{ + char *fn; + FILE *fp; + + fp = cvs_temp_file (&fn); + if (fp == NULL) + error (1, errno, "Failed to create temporary file"); + if (fclose (fp) == EOF) + error (0, errno, "Failed to close temporary file %s", fn); + return fn; +} + +/* Generate a unique temporary filename and return an open file stream + * to the truncated file by that name + * + * INPUTS + * filename where to place the pointer to the newly allocated file + * name string + * + * OUTPUTS + * filename dereferenced, will point to the newly allocated file + * name string. This value is undefined if the function + * returns an error. + * + * RETURNS + * An open file pointer to a read/write mode empty temporary file with the + * unique file name or NULL on failure. + * + * ERRORS + * On error, errno will be set to some value either by CVS_FOPEN or + * whatever system function is called to generate the temporary file name. + * The value of filename is undefined on error. + */ +FILE *cvs_temp_file (char **filename) +{ + char *fn; + FILE *fp; + int fd; + + /* FIXME - I'd like to be returning NULL here in noexec mode, but I think + * some of the rcs & diff functions which rely on a temp file run in + * noexec mode too. + */ + + assert (filename != NULL); + + fn = xmalloc (strlen (Tmpdir) + 11); + sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" ); + fd = mkstemp (fn); + + /* a NULL return will be interpreted by callers as an error and + * errno should still be set + */ + if (fd == -1) fp = NULL; + else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL) + { + /* Attempt to close and unlink the file since mkstemp returned + * sucessfully and we believe it's been created and opened. + */ + int save_errno = errno; + if (close (fd)) + error (0, errno, "Failed to close temporary file %s", fn); + if (CVS_UNLINK (fn)) + error (0, errno, "Failed to unlink temporary file %s", fn); + errno = save_errno; + } + + if (fp == NULL) free (fn); + + /* mkstemp is defined to open mode 0600 using glibc 2.0.7+. There used + * to be a complicated #ifdef checking the library versions here and then + * a chmod 0600 on the temp file for versions of glibc less than 2.1. This + * is rather a special case, leaves a race condition open regardless, and + * one could hope that sysadmins have read the relevant security + * announcements and upgraded by now to a version with a fix committed in + * January of 1999. + * + * If it is decided at some point that old, buggy versions of glibc should + * still be catered to, a umask of 0600 should be set before file creation + * instead then reset after file creation since this would avoid the race + * condition that the chmod left open to exploitation. + */ + + *filename = fn; + return fp; +} + + + +#ifdef HAVE_READLINK +/* char * + * xreadlink ( const char *link ) + * + * Like the X/OPEN and 4.4BSD readlink() function, but allocates and returns + * its own buf. + * + * INPUTS + * link The original path. + * + * RETURNS + * The resolution of the final symbolic link in the path. + * + * ERRORS + * This function exits with a fatal error if it fails to read the link for + * any reason. + */ +char * +xreadlink (const char *link) +{ + char *file = NULL; + int buflen = 128; + int link_name_len; + + /* Get the name of the file to which `from' is linked. + FIXME: what portability issues arise here? Are readlink & + ENAMETOOLONG defined on all systems? -twp */ + do + { + file = xrealloc (file, buflen); + link_name_len = readlink (link, file, buflen - 1); + buflen *= 2; + } + while (link_name_len < 0 && errno == ENAMETOOLONG); + + if (link_name_len < 0) + error (1, errno, "cannot readlink %s", link); + + file[link_name_len] = '\0'; + + return file; +} +#endif /* HAVE_READLINK */ + + + +/* char * + * xresolvepath ( const char *path ) + * + * Like xreadlink(), but resolve all links in a path. + * + * INPUTS + * path The original path. + * + * RETURNS + * The path with any symbolic links expanded. + * + * ERRORS + * This function exits with a fatal error if it fails to read the link for + * any reason. + */ +char * +xresolvepath (const char *path) +{ + char *hardpath; + char *owd; + + assert ( isdir ( path ) ); + + /* FIXME - If HAVE_READLINK is defined, we should probably walk the path + * bit by bit calling xreadlink(). + */ + + owd = xgetwd(); + if ( CVS_CHDIR ( path ) < 0) + error ( 1, errno, "cannot chdir to %s", path ); + if ( ( hardpath = xgetwd() ) == NULL ) + error (1, errno, "cannot getwd in %s", path); + if ( CVS_CHDIR ( owd ) < 0) + error ( 1, errno, "cannot chdir to %s", owd ); + free (owd); + return hardpath; +} + + + +/* Return a pointer into PATH's last component. */ +const char * +last_component (const char *path) +{ + const char *last = strrchr (path, '/'); + + if (last && (last != path)) + return last + 1; + else + return path; +} + + + +/* Return the home directory. Returns a pointer to storage + managed by this function or its callees (currently getenv). + This function will return the same thing every time it is + called. Returns NULL if there is no home directory. + + Note that for a pserver server, this may return root's home + directory. What typically happens is that upon being started from + inetd, before switching users, the code in cvsrc.c calls + get_homedir which remembers root's home directory in the static + variable. Then the switch happens and get_homedir might return a + directory that we don't even have read or execute permissions for + (which is bad, when various parts of CVS try to read there). One + fix would be to make the value returned by get_homedir only good + until the next call (which would free the old value). Another fix + would be to just always malloc our answer, and let the caller free + it (that is best, because some day we may need to be reentrant). + + The workaround is to put -f in inetd.conf which means that + get_homedir won't get called until after the switch in user ID. + + The whole concept of a "home directory" on the server is pretty + iffy, although I suppose some people probably are relying on it for + .cvsrc and such, in the cases where it works. */ +char * +get_homedir (void) +{ + static char *home = NULL; + char *env; + struct passwd *pw; + + if (home != NULL) + return home; + + if ( +#ifdef SERVER_SUPPORT + !server_active && +#endif + (env = getenv ("HOME")) != NULL) + home = env; + else if ((pw = (struct passwd *) getpwuid (getuid ())) + && pw->pw_dir) + home = xstrdup (pw->pw_dir); + else + return 0; + + return home; +} + +/* Compose a path to a file in the home directory. This is necessary because + * of different behavior on UNIX and VMS. See the notes in vms/filesubr.c. + * + * A more clean solution would be something more along the lines of a + * "join a directory to a filename" kind of thing which was not specific to + * the homedir. This should aid portability between UNIX, Mac, Windows, VMS, + * and possibly others. This is already handled by Perl - it might be + * interesting to see how much of the code was written in C since Perl is under + * the GPL and the Artistic license - we might be able to use it. + */ +char * +strcat_filename_onto_homedir (const char *dir, const char *file) +{ + char *path = xmalloc (strlen (dir) + 1 + strlen(file) + 1); + sprintf (path, "%s/%s", dir, file); + return path; +} + +/* See cvs.h for description. On unix this does nothing, because the + shell expands the wildcards. */ +void +expand_wild (int argc, char **argv, int *pargc, char ***pargv) +{ + int i; + if (size_overflow_p (xtimes (argc, sizeof (char *)))) { + *pargc = 0; + *pargv = NULL; + error (0, 0, "expand_wild: too many arguments"); + return; + } + *pargc = argc; + *pargv = xmalloc (xtimes (argc, sizeof (char *))); + 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 (const char *str1, const char *str2) +{ + const char *p; + const char *q; + int pqdiff; + + p = str1; + q = str2; + while ((pqdiff = tolower (*p) - tolower (*q)) == 0) + { + if (*p == '\0') + return 0; + ++p; + ++q; + } + return pqdiff; +} +#endif /* SERVER_SUPPORT */ diff --git a/contrib/cvs-1.12.9/src/find_names.c b/contrib/cvs-1.12.9/src/find_names.c new file mode 100644 index 0000000000..6a0dd69012 --- /dev/null +++ b/contrib/cvs-1.12.9/src/find_names.c @@ -0,0 +1,403 @@ +/* + * Copyright (c) 1992, Brian Berliner and Jeff Polk + * Copyright (c) 1989-1992, Brian Berliner + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * Find Names + * + * Finds all the pertinent file names, both from the administration and from the + * repository + * + * Find Dirs + * + * Finds all pertinent sub-directories of the checked out instantiation and the + * repository (and optionally the attic) + */ + +#include "cvs.h" + +static int find_dirs (char *dir, List * list, int checkadm, + List *entries); +static int find_rcs (char *dir, List * list); +static int add_subdir_proc (Node *, void *); +static int register_subdir_proc (Node *, void *); + +/* + * add the key from entry on entries list to the files list + */ +static int add_entries_proc (Node *, void *); +static int +add_entries_proc (Node *node, void *closure) +{ + Node *fnode; + List *filelist = closure; + Entnode *entnode = node->data; + + if (entnode->type != ENT_FILE) + return (0); + + fnode = getnode (); + fnode->type = FILES; + fnode->key = xstrdup (node->key); + if (addnode (filelist, fnode) != 0) + freenode (fnode); + return (0); +} + +/* Find files in the repository and/or working directory. On error, + may either print a nonfatal error and return NULL, or just give + a fatal error. On success, return non-NULL (even if it is an empty + list). */ + +List * +Find_Names (char *repository, int which, int aflag, List **optentries) +{ + List *entries; + List *files; + + /* make a list for the files */ + files = getlist (); + + /* look at entries (if necessary) */ + if (which & W_LOCAL) + { + /* parse the entries file (if it exists) */ + entries = Entries_Open (aflag, NULL); + if (entries != NULL) + { + /* walk the entries file adding elements to the files list */ + (void) walklist (entries, add_entries_proc, files); + + /* if our caller wanted the entries list, return it; else free it */ + if (optentries != NULL) + *optentries = entries; + else + Entries_Close (entries); + } + } + + if ((which & W_REPOS) && repository && !isreadable (CVSADM_ENTSTAT)) + { + /* search the repository */ + if (find_rcs (repository, files) != 0) + { + error (0, errno, "cannot open directory %s", repository); + goto error_exit; + } + + /* search the attic too */ + if (which & W_ATTIC) + { + char *dir; + dir = xmalloc (strlen (repository) + sizeof (CVSATTIC) + 10); + (void) sprintf (dir, "%s/%s", repository, CVSATTIC); + if (find_rcs (dir, files) != 0 + && !existence_error (errno)) + /* For now keep this a fatal error, seems less useful + for access control than the case above. */ + error (1, errno, "cannot open directory %s", dir); + free (dir); + } + } + + /* sort the list into alphabetical order and return it */ + sortlist (files, fsortcmp); + return (files); + error_exit: + dellist (&files); + return NULL; +} + +/* + * Add an entry from the subdirs list to the directories list. This + * is called via walklist. + */ + +static int +add_subdir_proc (Node *p, void *closure) +{ + List *dirlist = closure; + Entnode *entnode = p->data; + Node *dnode; + + 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 (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 (char *repository, int which, List *entries) +{ + List *dirlist; + + /* make a list for the directories */ + dirlist = getlist (); + + /* find the local ones */ + if (which & W_LOCAL) + { + List *tmpentries; + struct stickydirtag *sdtp; + + /* Look through the Entries file. */ + + if (entries != NULL) + tmpentries = entries; + else if (isfile (CVSADM_ENT)) + tmpentries = Entries_Open (0, NULL); + else + tmpentries = NULL; + + if (tmpentries != NULL) + sdtp = 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. */ + + /* FIXME-maybe: find_dirs is bogus for this usage because + it skips CVSATTIC and CVSLCK directories--those names + should be special only in the repository. However, in + the interests of not perturbing this code, we probably + should leave well enough alone unless we want to write + a sanity.sh test case (which would operate by manually + hacking on the CVS/Entries file). */ + + 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, entries) != 0) + error (1, errno, "cannot open directory %s", repository); + + /* 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 */ + sortlist (dirlist, fsortcmp); + return (dirlist); +} + +/* + * Finds all the ,v files in the argument directory, and adds them to the + * files list. Returns 0 for success and non-zero if the argument directory + * cannot be opened, in which case errno is set to indicate the error. + * In the error case LIST is left in some reasonable state (unchanged, or + * containing the files which were found before the error occurred). + */ +static int +find_rcs (char *dir, List *list) +{ + Node *p; + struct dirent *dp; + DIR *dirp; + + /* set up to read the dir */ + if ((dirp = CVS_OPENDIR (dir)) == NULL) + return (1); + + /* read the dir, grabbing the ,v files */ + errno = 0; + while ((dp = CVS_READDIR (dirp)) != NULL) + { + if (CVS_FNMATCH (RCSPAT, dp->d_name, 0) == 0) + { + char *comma; + + comma = strrchr (dp->d_name, ','); /* strip the ,v */ + *comma = '\0'; + p = getnode (); + p->type = FILES; + p->key = xstrdup (dp->d_name); + if (addnode (list, p) != 0) + freenode (p); + } + errno = 0; + } + if (errno != 0) + { + int save_errno = errno; + (void) CVS_CLOSEDIR (dirp); + errno = save_errno; + return 1; + } + (void) CVS_CLOSEDIR (dirp); + return (0); +} + +/* + * Finds all the subdirectories of the argument dir and adds them to + * the specified list. Sub-directories without a CVS administration + * directory are optionally ignored. If ENTRIES is not NULL, all + * files on the list are ignored. Returns 0 for success or 1 on + * error, in which case errno is set to indicate the error. + */ +static int +find_dirs (char *dir, List *list, int checkadm, List *entries) +{ + Node *p; + char *tmp = NULL; + size_t tmp_size = 0; + struct dirent *dp; + DIR *dirp; + int skip_emptydir = 0; + + /* First figure out whether we need to skip directories named + Emptydir. Except in the CVSNULLREPOS case, Emptydir is just + a normal directory name. */ + if (isabsolute (dir) + && strncmp (dir, current_parsed_root->directory, strlen (current_parsed_root->directory)) == 0 + && ISSLASH (dir[strlen (current_parsed_root->directory)]) + && strcmp (dir + strlen (current_parsed_root->directory) + 1, CVSROOTADM) == 0) + skip_emptydir = 1; + + /* set up to read the dir */ + if ((dirp = CVS_OPENDIR (dir)) == NULL) + return (1); + + /* read the dir, grabbing sub-dirs */ + errno = 0; + while ((dp = CVS_READDIR (dirp)) != NULL) + { + if (strcmp (dp->d_name, ".") == 0 || + strcmp (dp->d_name, "..") == 0 || + strcmp (dp->d_name, CVSATTIC) == 0 || + strcmp (dp->d_name, CVSLCK) == 0 || + strcmp (dp->d_name, CVSREP) == 0) + goto do_it_again; + + /* 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) + goto do_it_again; + + if (skip_emptydir + && strcmp (dp->d_name, CVSNULLREPOS) == 0) + goto do_it_again; + +#ifdef DT_DIR + if (dp->d_type != DT_DIR) + { + if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK) + goto do_it_again; +#endif + /* don't bother stating ,v files */ + if (CVS_FNMATCH (RCSPAT, dp->d_name, 0) == 0) + goto do_it_again; + + expand_string (&tmp, + &tmp_size, + strlen (dir) + strlen (dp->d_name) + 10); + sprintf (tmp, "%s/%s", dir, dp->d_name); + if (!isdir (tmp)) + goto do_it_again; + +#ifdef DT_DIR + } +#endif + + /* check for administration directories (if needed) */ + if (checkadm) + { + /* blow off symbolic links to dirs in local dir */ +#ifdef DT_DIR + if (dp->d_type != DT_DIR) + { + /* we're either unknown or a symlink at this point */ + if (dp->d_type == DT_LNK) + goto do_it_again; +#endif + /* Note that we only get here if we already set tmp + above. */ + if (islink (tmp)) + goto do_it_again; +#ifdef DT_DIR + } +#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)) + goto do_it_again; + } + + /* put it in the list */ + p = getnode (); + p->type = DIRS; + p->key = xstrdup (dp->d_name); + if (addnode (list, p) != 0) + freenode (p); + + do_it_again: + errno = 0; + } + if (errno != 0) + { + int save_errno = errno; + (void) CVS_CLOSEDIR (dirp); + errno = save_errno; + return 1; + } + (void) CVS_CLOSEDIR (dirp); + if (tmp != NULL) + free (tmp); + return (0); +} diff --git a/contrib/cvs-1.12.9/src/hardlink.c b/contrib/cvs-1.12.9/src/hardlink.c new file mode 100644 index 0000000000..828de06e1d --- /dev/null +++ b/contrib/cvs-1.12.9/src/hardlink.c @@ -0,0 +1,298 @@ +/* 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. */ + +/* Collect and manage hardlink info associated with a particular file. */ + +#include "cvs.h" +#include "hardlink.h" + +/* The structure currently used to manage hardlink info is a list. + Therefore, most of the functions which manipulate hardlink data + are walklist procedures. This is not a very efficient implementation; + if someone decides to use a real hash table (for instance), then + much of this code can be rewritten to be a little less arcane. + + Each element of `hardlist' represents an inode. It is keyed on the + inode number, and points to a list of files. This is to make it + easy to find out what files are linked to a given file FOO: find + FOO's inode, look it up in hardlist, and retrieve the list of files + associated with that inode. + + Each file node, in turn, is represented by a `hardlink_info' struct, + which includes `status' and `links' fields. The `status' field should + be used by a procedure like commit_fileproc or update_fileproc to + record each file's status; that way, after all file links have been + recorded, CVS can check the linkage of files which are in doubt + (i.e. T_NEEDS_MERGE files). + + TODO: a diagram of an example hardlist would help here. */ + +/* TODO: change this to something with a marginal degree of + efficiency, like maybe a hash table. Yeah. */ + +List *hardlist; /* Record hardlink information for working files */ +char *working_dir; /* The top-level working directory, used for + constructing full pathnames. */ + +/* Return a pointer to FILEPATH's node in the hardlist. This means + looking up its inode, retrieving the list of files linked to that + inode, and then looking up FILE in that list. If the file doesn't + seem to exist, return NULL. */ +Node * +lookup_file_by_inode (const char *filepath) +{ + char *inodestr, *file; + struct stat sb; + Node *hp, *p; + + /* Get file's basename, so that we can stat it. */ + file = strrchr (filepath, '/'); + if (file) + ++file; + else + file = (char *) filepath; + + /* inodestr contains the hexadecimal representation of an + inode, so it requires two bytes of text to represent + each byte of the inode number. */ + inodestr = (char *) xmalloc (2*sizeof(ino_t) + 1); + if( CVS_STAT( file, &sb ) < 0 ) + { + if (existence_error (errno)) + { + /* The file doesn't exist; we may be doing an update on a + file that's been removed. A nonexistent file has no + link information, so return without changing hardlist. */ + free (inodestr); + return NULL; + } + error (1, errno, "cannot stat %s", file); + } + + sprintf (inodestr, "%lx", (unsigned long) sb.st_ino); + + /* Find out if this inode is already in the hardlist, adding + a new entry to the list if not. */ + hp = findnode (hardlist, inodestr); + if (hp == NULL) + { + hp = getnode (); + hp->type = NT_UNKNOWN; + hp->key = inodestr; + hp->data = getlist(); + hp->delproc = dellist; + (void) addnode (hardlist, hp); + } + else + { + free (inodestr); + } + + p = findnode (hp->data, filepath); + if (p == NULL) + { + p = getnode(); + p->type = NT_UNKNOWN; + p->key = xstrdup (filepath); + p->data = NULL; + (void) addnode (hp->data, p); + } + + return p; +} + +/* After a file has been checked out, add a node for it to the hardlist + (if necessary) and mark it as checked out. */ +void +update_hardlink_info (const char *file) +{ + char *path; + Node *n; + struct hardlink_info *hlinfo; + + if (file[0] == '/') + { + path = xstrdup (file); + } + else + { + /* file is a relative pathname; assume it's from the current + working directory. */ + char *dir = xgetwd(); + path = xmalloc (strlen(dir) + strlen(file) + 2); + sprintf (path, "%s/%s", dir, file); + free (dir); + } + + n = lookup_file_by_inode (path); + if (n == NULL) + { + /* Something is *really* wrong if the file doesn't exist here; + update_hardlink_info should be called only when a file has + just been checked out to a working directory. */ + error (1, 0, "lost hardlink info for %s", file); + } + + if (n->data == NULL) + n->data = xmalloc (sizeof (struct hardlink_info)); + hlinfo = n->data; + hlinfo->status = T_UPTODATE; + hlinfo->checked_out = 1; +} + +/* Return a List with all the files known to be linked to FILE in + the working directory. Used by special_file_mismatch, to determine + whether it is safe to merge two files. + + FIXME: What is the memory allocation for the return value? We seem + to sometimes allocate a new list (getlist() call below) and sometimes + return an existing list (where we return n->data). */ +List * +list_linked_files_on_disk (char *file) +{ + char *inodestr, *path; + struct stat sb; + Node *n; + + /* If hardlist is NULL, we have not been doing an operation that + would permit us to know anything about the file's hardlinks + (cvs update, cvs commit, etc). Return an empty list. */ + if (hardlist == NULL) + return getlist(); + + /* Get the full pathname of file (assuming the working directory) */ + if (file[0] == '/') + path = xstrdup (file); + else + { + char *dir = xgetwd(); + path = (char *) xmalloc (strlen(dir) + strlen(file) + 2); + sprintf (path, "%s/%s", dir, file); + free (dir); + } + + /* We do an extra lookup_file here just to make sure that there + is a node for `path' in the hardlist. If that were not so, + comparing the working directory linkage against the repository + linkage for a file would always fail. */ + (void) lookup_file_by_inode (path); + + if( CVS_STAT( path, &sb ) < 0 ) + error (1, errno, "cannot stat %s", file); + /* inodestr contains the hexadecimal representation of an + inode, so it requires two bytes of text to represent + each byte of the inode number. */ + inodestr = (char *) xmalloc (2*sizeof(ino_t) + 1); + sprintf (inodestr, "%lx", (unsigned long) sb.st_ino); + + /* Make sure the files linked to this inode are sorted. */ + n = findnode (hardlist, inodestr); + sortlist (n->data, fsortcmp); + + free (inodestr); + return n->data; +} + +/* Compare the files in the `key' fields of two lists, returning 1 if + the lists are equivalent and 0 otherwise. + + Only the basenames of each file are compared. This is an awful hack + that exists because list_linked_files_on_disk returns full paths + and the `hardlinks' structure of a RCSVers node contains only + basenames. That in turn is a result of the awful hack that only + basenames are stored in the RCS file. If anyone ever solves the + problem of correctly managing cross-directory hardlinks, this + function (along with most functions in this file) must be fixed. */ + +int +compare_linkage_lists (List *links1, List *links2) +{ + Node *n1, *n2; + char *p1, *p2; + + sortlist (links1, fsortcmp); + sortlist (links2, fsortcmp); + + n1 = links1->list->next; + n2 = links2->list->next; + + while (n1 != links1->list && n2 != links2->list) + { + /* Get the basenames of both files. */ + p1 = strrchr (n1->key, '/'); + if (p1 == NULL) + p1 = n1->key; + else + ++p1; + + p2 = strrchr (n2->key, '/'); + if (p2 == NULL) + p2 = n2->key; + else + ++p2; + + /* Compare the files' basenames. */ + if (strcmp (p1, p2) != 0) + return 0; + + n1 = n1->next; + n2 = n2->next; + } + + /* At this point we should be at the end of both lists; if not, + one file has more links than the other, and return 1. */ + return (n1 == links1->list && n2 == links2->list); +} + +/* Find a checked-out file in a list of filenames. Used by RCS_checkout + when checking out a new hardlinked file, to decide whether this file + can be linked to any others that already exist. The return value + is not currently used. */ + +int +find_checkedout_proc (Node *node, void *data) +{ + Node **uptodate = (Node **) data; + Node *link; + char *dir = xgetwd(); + char *path; + struct hardlink_info *hlinfo; + + /* If we have already found a file, don't do anything. */ + if (*uptodate != NULL) + return 0; + + /* Look at this file in the hardlist and see whether the checked_out + field is 1, meaning that it has been checked out during this CVS run. */ + path = (char *) + xmalloc (strlen (dir) + strlen (node->key) + 2); + sprintf (path, "%s/%s", dir, node->key); + link = lookup_file_by_inode (path); + free (path); + free (dir); + + if (link == NULL) + { + /* We haven't seen this file -- maybe it hasn't been checked + out yet at all. */ + return 0; + } + + hlinfo = link->data; + if (hlinfo->checked_out) + { + /* This file has been checked out recently, so it's safe to + link to it. */ + *uptodate = link; + } + + return 0; +} + diff --git a/contrib/cvs-1.12.9/src/hardlink.h b/contrib/cvs-1.12.9/src/hardlink.h new file mode 100644 index 0000000000..add397df1b --- /dev/null +++ b/contrib/cvs-1.12.9/src/hardlink.h @@ -0,0 +1,33 @@ +/* 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. */ + +/* Data type definitions and declarations for hardlink management. */ + +/* This file should be #included in CVS source files after cvs.h + since it relies on types and macros defined there. */ + +/* The `checked_out' member of a hardlink_info struct is used only + when files are being checked out or updated. It is used only when + hardlinked files are being checked out. */ + +struct hardlink_info +{ + Ctype status; /* as returned from Classify_File() */ + int checked_out; /* has this file been checked out lately? */ +}; + +extern List *hardlist; +extern char *working_dir; + +Node *lookup_file_by_inode (const char *); +void update_hardlink_info (const char *); +List *list_linked_files_on_disk (char *); +int compare_linkage_lists (List *, List *); +int find_checkedout_proc (Node *, void *); diff --git a/contrib/cvs-1.12.9/src/hash.c b/contrib/cvs-1.12.9/src/hash.c new file mode 100644 index 0000000000..5760ec0964 --- /dev/null +++ b/contrib/cvs-1.12.9/src/hash.c @@ -0,0 +1,551 @@ +/* + * Copyright (c) 1992, Brian Berliner and Jeff Polk + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * Polk's hash list manager. So cool. + */ + +#include "cvs.h" + +/* 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; + +static void freenode_mem (Node * p); + +/* hash function */ +static int +hashp (const char *key) +{ + unsigned int h = 0; + unsigned int g; + + assert(key != NULL); + + while (*key != 0) + { + unsigned int c = *key++; + /* The FOLD_FN_CHAR is so that findnode_fn works. */ + h = (h << 4) + FOLD_FN_CHAR (c); + if ((g = h & 0xf0000000) != 0) + h = (h ^ (g >> 24)) ^ g; + } + + return h % HASHSIZE; +} + + + +/* + * create a new list (or get an old one from the cache) + */ +List * +getlist (void) +{ + int i; + List *list; + Node *node; + + if (listcache != NULL) + { + /* get a list from the cache and clear it */ + list = listcache; + listcache = listcache->next; + list->next = (List *) NULL; + for (i = 0; i < HASHSIZE; i++) + list->hasharray[i] = (Node *) NULL; + } + else + { + /* make a new list from scratch */ + list = xmalloc (sizeof (List)); + memset (list, 0, sizeof (List)); + node = getnode (); + list->list = node; + node->type = HEADER; + node->next = node->prev = node; + } + return list; +} + + + +/* + * Free up a list. For accessing globals which might be accessed via interrupt + * handlers, it can be assumed that the first action of this function will be + * to set the *listp to NULL. + */ +void +dellist (List **listp) +{ + int i; + Node *p; + List *tmp; + + if (*listp == NULL) + return; + + tmp = *listp; + *listp = NULL; + + p = tmp->list; + + /* free each node in the list (except header) */ + while (p->next != p) + delnode (p->next); + + /* free any list-private data, without freeing the actual header */ + freenode_mem (p); + + /* free up the header nodes for hash lists (if any) */ + for (i = 0; i < HASHSIZE; i++) + { + if ((p = tmp->hasharray[i]) != NULL) + { + /* put the nodes into the cache */ +#ifndef NOCACHE + p->type = NT_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 + tmp->next = listcache; + listcache = tmp; +#else + free (tmp->list); + free (tmp); +#endif +} + + + +/* + * get a new list node + */ +Node * +getnode (void) +{ + Node *p; + + if (nodecache != (Node *) NULL) + { + /* get one from the cache */ + p = nodecache; + nodecache = p->next; + } + else + { + /* make a new one */ + p = xmalloc (sizeof (Node)); + } + + /* always make it clean */ + memset (p, 0, sizeof (Node)); + p->type = NT_UNKNOWN; + + return p; +} + + + +/* + * remove a node from it's list (maybe hash list too) and free it + */ +void +delnode (Node *p) +{ + if (p == NULL) + return; + + /* take it out of the list */ + p->next->prev = p->prev; + p->prev->next = p->next; + + /* if it was hashed, remove it from there too */ + if (p->hashnext != NULL) + { + p->hashnext->hashprev = p->hashprev; + p->hashprev->hashnext = p->hashnext; + } + + /* free up the storage */ + freenode (p); +} + + + +/* + * free up the storage associated with a node + */ +static void +freenode_mem (Node *p) +{ + if (p->delproc != NULL) + p->delproc (p); /* call the specified delproc */ + else + { + if (p->data != NULL) /* otherwise free() it if necessary */ + free (p->data); + } + if (p->key != NULL) /* free the key if necessary */ + free (p->key); + + /* to be safe, re-initialize these */ + p->key = p->data = NULL; + p->delproc = NULL; +} + + + +/* + * free up the storage associated with a node and recycle it + */ +void +freenode (Node *p) +{ + /* first free the memory */ + freenode_mem (p); + + /* then put it in the cache */ +#ifndef NOCACHE + p->type = NT_UNKNOWN; + p->next = nodecache; + nodecache = p; +#else + free (p); +#endif +} + + + +/* + * Link item P into list LIST before item MARKER. If P->KEY is non-NULL and + * that key is already in the hash table, return -1 without modifying any + * parameter. + * + * return 0 on success + */ +int +insert_before (List *list, Node *marker, Node *p) +{ + if (p->key != NULL) /* hash it too? */ + { + int hashval; + Node *q; + + hashval = hashp (p->key); + if (list->hasharray[hashval] == NULL) /* make a header for list? */ + { + q = getnode (); + q->type = HEADER; + list->hasharray[hashval] = q->hashnext = q->hashprev = q; + } + + /* put it into the hash list if it's not already there */ + for (q = list->hasharray[hashval]->hashnext; + q != list->hasharray[hashval]; q = q->hashnext) + { + if (strcmp (p->key, q->key) == 0) + return -1; + } + q = list->hasharray[hashval]; + p->hashprev = q->hashprev; + p->hashnext = q; + p->hashprev->hashnext = p; + q->hashprev = p; + } + + p->next = marker; + p->prev = marker->prev; + marker->prev->next = p; + marker->prev = p; + + return 0; +} + + + +/* + * insert item p at end of list "list" (maybe hash it too) if hashing and it + * already exists, return -1 and don't actually put it in the list + * + * return 0 on success + */ +int +addnode (List *list, Node *p) +{ + return insert_before (list, list->list, p); +} + + + +/* + * Like addnode, but insert p at the front of `list'. This bogosity is + * necessary to preserve last-to-first output order for some RCS functions. + */ +int +addnode_at_front (List *list, Node *p) +{ + return insert_before (list, list->list->next, p); +} + + + +/* Look up an entry in hash list table and return a pointer to the + * node. Return NULL if not found or if list is NULL. Abort with a fatal + * error for errors. + */ +Node * +findnode (List *list, const char *key) +{ + Node *head, *p; + + if ((list == NULL)) + return NULL; + + assert (key != NULL); + + head = list->hasharray[hashp (key)]; + if (head == NULL) + /* Not found. */ + return NULL; + + for (p = head->hashnext; p != head; p = p->hashnext) + if (strcmp (p->key, key) == 0) + return p; + return NULL; +} + + + +/* + * Like findnode, but for a filename. + */ +Node * +findnode_fn (List *list, const char *key) +{ + Node *head, *p; + + /* This probably should be "assert (list != NULL)" (or if not we + should document the current behavior), but only if we check all + the callers to see if any are relying on this behavior. */ + if (list == NULL) + return NULL; + + assert (key != NULL); + + head = list->hasharray[hashp (key)]; + if (head == NULL) + return NULL; + + for (p = head->hashnext; p != head; p = p->hashnext) + if (fncmp (p->key, key) == 0) + return p; + return NULL; +} + + + +/* + * walk a list with a specific proc + */ +int +walklist (List *list, int (*proc) (Node *, void *), void *closure) +{ + Node *head, *p; + int err = 0; + +#ifdef HAVE_PRINTF_PTR + TRACE (TRACE_FLOW, "walklist ( list=%p, proc=%p, closure=%p )", + (void *)list, (void *)proc, (void *)closure); +#else + TRACE (TRACE_FLOW, "walklist ( list=%lx, proc=%lx, closure=%lx )", + (unsigned long)list,(unsigned long) proc, + (unsigned long)closure); +#endif + + if (list == NULL) + return 0; + + head = list->list; + for (p = head->next; p != head; p = p->next) + err += proc (p, closure); + return err; +} + + + +int +list_isempty (List *list) +{ + return list == NULL || list->list->next == list->list; +} + + + +static int (*client_comp) (const Node *, const Node *); + +static int +qsort_comp (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) + */ +void +sortlist (List *list, int (*comp) (const Node *, const Node *)) +{ + Node *head, *remain, *p, **array; + int i, n; + + if (list == NULL) + return; + + /* save the old first element of the list */ + head = list->list; + remain = head->next; + + /* count the number of nodes in the list */ + n = 0; + for (p = remain; p != head; p = p->next) + n++; + + /* 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; + + /* 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); +} + + + +/* + * compare two files list node (for sort) + */ +int +fsortcmp (const Node *p, const Node *q) +{ + return strcmp (p->key, q->key); +} + + + +/* Debugging functions. Quite useful to call from within gdb. */ + + +static char * +nodetypestring (Ntype type) +{ + switch (type) { + case NT_UNKNOWN: return "UNKNOWN"; + case HEADER: return "HEADER"; + case ENTRIES: return "ENTRIES"; + case FILES: return "FILES"; + case LIST: return "LIST"; + case RCSNODE: return "RCSNODE"; + case RCSVERS: return "RCSVERS"; + case DIRS: return "DIRS"; + case UPDATE: return "UPDATE"; + case LOCK: return "LOCK"; + case NDBMNODE: return "NDBMNODE"; + case FILEATTR: return "FILEATTR"; + case VARIABLE: return "VARIABLE"; + case RCSFIELD: return "RCSFIELD"; + case RCSCMPFLD: return "RCSCMPFLD"; + } + + return ""; +} + + + +static int +printnode (Node *node, void *closure) +{ + if (node == NULL) + { + (void) printf("NULL node.\n"); + return 0; + } + +#ifdef HAVE_PRINTF_PTR + (void) printf("Node at %p: type = %s, key = %p = \"%s\", data = %p, next = %p, prev = %p\n", + (void *) node, nodetypestring(node->type), + (void *) node->key, node->key, node->data, + (void *) node->next, (void *) node->prev); +#else + (void) printf("Node at 0x%lx: type = %s, key = 0x%lx = \"%s\", data = 0x%lx, next = 0x%lx, prev = 0x%lx\n", + (unsigned long) node, nodetypestring(node->type), + (unsigned long) node->key, node->key, (unsigned long) node->data, + (unsigned long) node->next, (unsigned long) node->prev); +#endif + + 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 (List *list) +{ + if (list == NULL) + { + (void) printf("NULL list.\n"); + return; + } + +#ifdef HAVE_PRINTF_PTR + (void) printf("List at %p: list = %p, HASHSIZE = %d, next = %p\n", + (void *) list, (void *) list->list, HASHSIZE, (void *) list->next); +#else + (void) printf("List at 0x%lx: list = 0x%lx, HASHSIZE = %d, next = 0x%lx\n", + (unsigned long) list, (unsigned long) list->list, HASHSIZE, + (unsigned long) list->next); +#endif + + (void) walklist(list, printnode, NULL); + + return; +} diff --git a/contrib/cvs-1.12.9/src/hash.h b/contrib/cvs-1.12.9/src/hash.h new file mode 100644 index 0000000000..a27b717c10 --- /dev/null +++ b/contrib/cvs-1.12.9/src/hash.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1992, Brian Berliner and Jeff Polk + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + */ + +/* + * The number of buckets for the hash table contained in each list. This + * should probably be prime. + */ +#define HASHSIZE 151 + +/* + * Types of nodes + */ +enum ntype +{ + NT_UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE, + RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE, FILEATTR, + VARIABLE, RCSFIELD, RCSCMPFLD +}; +typedef enum ntype Ntype; + +struct node +{ + Ntype type; + struct node *next; + struct node *prev; + struct node *hashnext; + struct node *hashprev; + char *key; + void *data; + void (*delproc) (); +}; +typedef struct node Node; + +struct list +{ + Node *list; + Node *hasharray[HASHSIZE]; + struct list *next; +}; +typedef struct list List; + +List *getlist (void); +Node *findnode (List * list, const char *key); +Node *findnode_fn (List * list, const char *key); +Node *getnode (void); +int insert_before (List * list, Node * marker, Node * p); +int addnode (List * list, Node * p); +int addnode_at_front (List * list, Node * p); +int walklist (List * list, int (*)(Node *n, void *closure), void *closure); +int list_isempty (List *list); +void dellist (List ** listp); +void delnode (Node * p); +void freenode (Node * p); +void sortlist (List * list, int (*)(const Node *, const Node *)); +int fsortcmp (const Node * p, const Node * q); diff --git a/contrib/cvs-1.12.9/src/history.c b/contrib/cvs-1.12.9/src/history.c new file mode 100644 index 0000000000..8421474651 --- /dev/null +++ b/contrib/cvs-1.12.9/src/history.c @@ -0,0 +1,1621 @@ +/* + * + * You may distribute under the terms of the GNU General Public License + * as specified in the README file that comes with the CVS 1.0 kit. + * + * **************** History of Users and Module **************** + * + * LOGGING: Append record to "${CVSROOT}/CVSROOTADM/CVSROOTADM_HISTORY". + * + * On For each Tag, Add, Checkout, Commit, Update or Release command, + * one line of text is written to a History log. + * + * X date | user | CurDir | special | rev(s) | argument '\n' + * + * where: [The spaces in the example line above are not in the history file.] + * + * 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. + * P "Update" cmd - User file was patched. + * G "Update" cmd - File was merged successfully. + * C "Update" cmd - File was merged and shows overlaps. + * M "Commit" cmd - "Modified" file. + * A "Commit" cmd - "Added" file. + * R "Commit" cmd - "Removed" file. + * + * date is a fixed length 8-char hex representation of a Unix time_t. + * [Starting here, variable fields are delimited by '|' chars.] + * + * user is the username of the person who typed the command. + * + * CurDir The directory where the action occurred. This should be the + * absolute path of the directory which is at the same level as + * the "Repository" field (for W,U,P,G,C & M,A,R). + * + * Repository For record types [W,U,P,G,C,M,A,R] this field holds the + * repository read from the administrative data where the + * command was typed. + * T "A" --> New Tag, "D" --> Delete Tag + * Otherwise it is the Tag or Date to modify. + * O,F,E A "" (null field) + * + * rev(s) Revision number or tag. + * T The Tag to apply. + * 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,P 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 [TOEF]) or file (for [WUPGCMAR]) affected. + * + * + *** Report categories: "User" and "Since" modifiers apply to all reports. + * [For "sort" ordering see the "sort_order" routine.] + * + * Extract list of record types + * + * -e, -x [TOEFWUPGCMAR] + * + * Extracted records are simply printed, No analysis is performed. + * All "field" modifiers apply. -e chooses all types. + * + * Checked 'O'ut modules + * + * -o, -w + * Checked out modules. 'F' and 'O' records are examined and if + * the last record for a repository/file is an 'O', a line is + * printed. "-w" forces the "working dir" to be used in the + * comparison instead of the repository. + * + * Committed (Modified) files + * + * -c, -l, -w + * All 'M'odified, 'A'dded and 'R'emoved records are examined. + * "Field" modifiers apply. -l forces a sort by file within user + * and shows only the last modifier. -w works as in Checkout. + * + * Warning: Be careful with what you infer from the output of + * "cvs hi -c -l". It means the last time *you* + * changed the file, not the list of files for which + * you were the last changer!!! + * + * Module history for named modules. + * -m module, -l + * + * This is special. If one or more modules are specified, the + * module names are remembered and the files making up the + * modules are remembered. Only records matching exactly those + * files and repositories are shown. Sorting by "module", then + * filename, is implied. If -l ("last modified") is specified, + * then "update" records (types WUPCG), tag and release records + * are ignored and the last (by date) "modified" record. + * + * TAG history + * + * -T All Tag records are displayed. + * + *** Modifiers. + * + * Since ... [All records contain a timestamp, so any report + * category can be limited by date.] + * + * -D date - The "date" is parsed into a Unix "time_t" and + * records with an earlier time stamp are ignored. + * -r rev/tag - A "rev" begins with a digit. A "tag" does not. If + * you use this option, every file is searched for the + * indicated rev/tag. + * -t tag - The "tag" is searched for in the history file and no + * record is displayed before the tag is found. An + * error is printed if the tag is never found. + * -b string - Records are printed only back to the last reference + * to the string in the "module", "file" or + * "repository" fields. + * + * Field Selections [Simple comparisons on existing fields. All field + * selections are repeatable.] + * + * -a - All users. + * -u user - If no user is given and '-a' is not given, only + * records for the user typing the command are shown. + * ==> If -a or -u is not specified, just use "self". + * + * -f filematch - Only records in which the "file" field contains the + * string "filematch" are considered. + * + * -p repository - Only records in which the "repository" string is a + * prefix of the "repos" field are considered. + * + * -n modulename - Only records which contain "modulename" in the + * "module" field are considered. + * + * + * EXAMPLES: ("cvs history", "cvs his" or "cvs hi") + * + *** Checked out files for username. (default self, e.g. "dgg") + * cvs hi [equivalent to: "cvs hi -o -u dgg"] + * cvs hi -u user [equivalent to: "cvs hi -o -u user"] + * cvs hi -o [equivalent to: "cvs hi -o -u dgg"] + * + *** Committed (modified) files from the beginning of the file. + * cvs hi -c [-u user] + * + *** Committed (modified) files since Midnight, January 1, 1990: + * cvs hi -c -D 'Jan 1 1990' [-u user] + * + *** Committed (modified) files since tag "TAG" was stored in the history file: + * cvs hi -c -t TAG [-u user] + * + *** Committed (modified) files since tag "TAG" was placed on the files: + * cvs hi -c -r TAG [-u user] + * + *** Who last committed file/repository X? + * cvs hi -c -l -[fp] X + * + *** Modified files since tag/date/file/repos? + * cvs hi -c {-r TAG | -D Date | -b string} + * + *** Tag history + * cvs hi -T + * + *** History of file/repository/module X. + * cvs hi -[fpn] X + * + *** History of user "user". + * cvs hi -e -u user + * + *** Dump (eXtract) specified record types + * cvs hi -x [TOEFWUPGCMAR] + * + * + * FUTURE: J[Join], I[Import] (Not currently implemented.) + * + */ + +#include "cvs.h" +#include "history.h" +#include "savecwd.h" + +static struct hrec +{ + char *type; /* Type of record (In history record) */ + char *user; /* Username (In history record) */ + char *dir; /* "Compressed" Working dir (In history record) */ + char *repos; /* (Tag is special.) Repository (In history record) */ + char *rev; /* Revision affected (In history record) */ + char *file; /* Filename (In history record) */ + char *end; /* Ptr into repository to copy at end of workdir */ + char *mod; /* The module within which the file is contained */ + time_t date; /* Calculated from date stored in record */ + long idx; /* Index of record, for "stable" sort. */ +} *hrec_head; +static long hrec_idx; + + +static void fill_hrec (char *line, struct hrec * hr); +static int accept_hrec (struct hrec * hr, struct hrec * lr); +static int select_hrec (struct hrec * hr); +static int sort_order (const void *l, const void *r); +static int within (char *find, char *string); +static void expand_modules (void); +static void read_hrecs (char *fname); +static void report_hrecs (void); +static void save_file (char *dir, char *name, char *module); +static void save_module (char *module); +static void save_user (char *name); + +#define USER_INCREMENT 2 +#define FILE_INCREMENT 128 +#define MODULE_INCREMENT 5 +#define HREC_INCREMENT 128 + +static short report_count; + +static short extract; +static short extract_all; +static short v_checkout; +static short modified; +static short tag_report; +static short module_report; +static short working; +static short last_entry; +static short all_users; + +static short user_sort; +static short repos_sort; +static short file_sort; +static short module_sort; + +static short tz_local; +static time_t tz_seconds_east_of_GMT; +static char *tz_name = "+0000"; + +char *logHistory = ALL_HISTORY_REC_TYPES; + +/* -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. RCS format. */ +static char * since_date; + +static struct hrec *last_since_tag; +static struct hrec *last_backto; + +/* 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; + +static char **user_list; /* Ptr to array of ptrs to user names */ +static int user_max; /* Number of elements allocated */ +static int user_count; /* Number of elements used */ + +static struct file_list_str +{ + char *l_file; + char *l_module; +} *file_list; /* Ptr to array file name structs */ +static int file_max; /* Number of elements allocated */ +static int file_count; /* Number of elements used */ + +static char **mod_list; /* Ptr to array of ptrs to module names */ +static int mod_max; /* Number of elements allocated */ +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", + " Reports:\n", + " -T Produce report on all TAGs\n", + " -c Committed (Modified) files\n", + " -o Checked out modules\n", + " -m Look for specified module (repeatable)\n", + " -x [" ALL_HISTORY_REC_TYPES "] Extract by record type\n", + " -e Everything (same as -x, but all record types)\n", + " Flags:\n", + " -a All users (Default is self)\n", + " -l Last modified (committed or modified report)\n", + " -w Working directory must match\n", + " Options:\n", + " -D Since date (Many formats)\n", + " -b Back to record with str in module/file/repos field\n", + " -f Specified file (same as command line) (repeatable)\n", + " -n In module (repeatable)\n", + " -p In repository (repeatable)\n", + " -r Since rev or tag (looks inside RCS files!)\n", + " -t Since tag record placed in history file (by anyone).\n", + " -u For user name (repeatable)\n", + " -z Output for time zone (e.g. -z -0700)\n", + NULL}; + +/* Sort routine for qsort: + - If a user is selected at all, sort it first. User-within-file is useless. + - If a module was selected explicitly, sort next on module. + - Then sort by file. "File" is "repository/file" unless "working" is set, + then it is "workdir/file". (Revision order should always track date.) + - Always sort timestamp last. +*/ +static int +sort_order (const void *l, const void *r) +{ + int i; + const struct hrec *left = (const struct hrec *) l; + const struct hrec *right = (const struct hrec *) r; + + if (user_sort) /* If Sort by username, compare users */ + { + if ((i = strcmp (left->user, right->user)) != 0) + return (i); + } + if (module_sort) /* If sort by modules, compare module names */ + { + if (left->mod && right->mod) + if ((i = strcmp (left->mod, right->mod)) != 0) + return (i); + } + if (repos_sort) /* If sort by repository, compare them. */ + { + if ((i = strcmp (left->repos, right->repos)) != 0) + return (i); + } + if (file_sort) /* If sort by filename, compare files, NOT dirs. */ + { + if ((i = strcmp (left->file, right->file)) != 0) + return (i); + + if (working) + { + if ((i = strcmp (left->dir, right->dir)) != 0) + return (i); + + if ((i = strcmp (left->end, right->end)) != 0) + return (i); + } + } + + /* + * By default, sort by date, time + * XXX: This fails after 2030 when date slides into sign bit + */ + if ((i = ((long) (left->date) - (long) (right->date))) != 0) + return (i); + + /* For matching dates, keep the sort stable by using record index */ + return (left->idx - right->idx); +} + +int +history (int argc, char **argv) +{ + int i, c; + char *fname; + + if (argc == -1) + usage (history_usg); + + since_rev = xstrdup (""); + since_tag = xstrdup (""); + backto = xstrdup (""); + rec_types = xstrdup (""); + optind = 0; + while ((c = getopt (argc, argv, "+Tacelow?D:b:f:m:n:p:r:t:u:x:X:z:")) != -1) + { + switch (c) + { + case 'T': /* Tag list */ + report_count++; + tag_report++; + break; + case 'a': /* For all usernames */ + all_users++; + break; + case 'c': + report_count++; + modified = 1; + break; + case 'e': + report_count++; + extract_all++; + free (rec_types); + rec_types = xstrdup (ALL_HISTORY_REC_TYPES); + break; + case 'l': /* Find Last file record */ + last_entry = 1; + break; + case 'o': + report_count++; + v_checkout = 1; + break; + case 'w': /* Match Working Dir (CurDir) fields */ + working = 1; + break; + case 'X': /* Undocumented debugging flag */ +#ifdef DEBUG + histfile = optarg; +#endif + break; + + case 'D': /* Since specified date */ + if (*since_rev || *since_tag || *backto) + { + error (0, 0, "date overriding rev/tag/backto"); + *since_rev = *since_tag = *backto = '\0'; + } + since_date = Make_Date (optarg); + break; + case 'b': /* Since specified file/Repos */ + if (since_date || *since_rev || *since_tag) + { + error (0, 0, "backto overriding date/rev/tag"); + *since_rev = *since_tag = '\0'; + if (since_date != NULL) + free (since_date); + since_date = NULL; + } + free (backto); + backto = xstrdup (optarg); + break; + case 'f': /* For specified file */ + save_file ("", optarg, (char *) NULL); + break; + case 'm': /* Full module report */ + if (!module_report++) report_count++; + /* fall through */ + case 'n': /* Look for specified module */ + save_module (optarg); + break; + case 'p': /* For specified directory */ + save_file (optarg, "", (char *) NULL); + break; + case 'r': /* Since specified Tag/Rev */ + if (since_date || *since_tag || *backto) + { + error (0, 0, "rev overriding date/tag/backto"); + *since_tag = *backto = '\0'; + if (since_date != NULL) + free (since_date); + since_date = NULL; + } + free (since_rev); + since_rev = xstrdup (optarg); + break; + case 't': /* Since specified Tag/Rev */ + if (since_date || *since_rev || *backto) + { + error (0, 0, "tag overriding date/marker/file/repos"); + *since_rev = *backto = '\0'; + if (since_date != NULL) + free (since_date); + since_date = NULL; + } + free (since_tag); + since_tag = xstrdup (optarg); + break; + case 'u': /* For specified username */ + save_user (optarg); + break; + case 'x': + report_count++; + extract++; + { + char *cp; + + for (cp = optarg; *cp; cp++) + if (!strchr (ALL_HISTORY_REC_TYPES, *cp)) + error (1, 0, "%c is not a valid report type", *cp); + } + free (rec_types); + rec_types = xstrdup (optarg); + break; + case 'z': + tz_local = + (optarg[0] == 'l' || optarg[0] == 'L') + && (optarg[1] == 't' || optarg[1] == 'T') + && !optarg[2]; + if (tz_local) + tz_name = optarg; + else + { + /* + * Convert a known time with the given timezone to time_t. + * Use the epoch + 23 hours, so timezones east of GMT work. + */ + static char f[] = "1/1/1970 23:00 %s"; + char *buf = xmalloc (sizeof (f) - 2 + strlen (optarg)); + time_t t; + sprintf (buf, f, optarg); + t = get_date (buf, (struct timeb *) NULL); + free (buf); + if (t == (time_t) -1) + error (0, 0, "%s is not a known time zone", optarg); + else + { + /* + * Convert to seconds east of GMT, removing the + * 23-hour offset mentioned above. + */ + tz_seconds_east_of_GMT = (time_t)23 * 60 * 60 - t; + tz_name = optarg; + } + } + break; + case '?': + default: + usage (history_usg); + break; + } + } + argc -= optind; + argv += optind; + for (i = 0; i < argc; i++) + save_file ("", argv[i], (char *) NULL); + + + /* ================ Now analyze the arguments a bit */ + if (!report_count) + v_checkout++; + else if (report_count > 1) + error (1, 0, "Only one report type allowed from: \"-Tcomxe\"."); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + struct file_list_str *f1; + char **mod; + + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (tag_report) + send_arg("-T"); + if (all_users) + send_arg("-a"); + if (modified) + send_arg("-c"); + if (last_entry) + send_arg("-l"); + if (v_checkout) + send_arg("-o"); + if (working) + send_arg("-w"); + if (histfile) + send_arg("-X"); + if (since_date) + client_senddate (since_date); + if (backto[0] != '\0') + option_with_arg ("-b", backto); + for (f1 = file_list; f1 < &file_list[file_count]; ++f1) + { + if (f1->l_file[0] == '*') + option_with_arg ("-p", f1->l_file + 1); + else + option_with_arg ("-f", f1->l_file); + } + if (module_report) + send_arg("-m"); + for (mod = mod_list; mod < &mod_list[mod_count]; ++mod) + option_with_arg ("-n", *mod); + if (*since_rev) + option_with_arg ("-r", since_rev); + if (*since_tag) + option_with_arg ("-t", since_tag); + for (mod = user_list; mod < &user_list[user_count]; ++mod) + option_with_arg ("-u", *mod); + if (extract_all) + send_arg("-e"); + if (extract) + option_with_arg ("-x", rec_types); + option_with_arg ("-z", tz_name); + + send_to_server ("history\012", 0); + return get_responses_and_close (); + } +#endif + + if (all_users) + save_user (""); + + if (mod_list) + expand_modules (); + + 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 || extract_all) + { + if (user_list) + user_sort++; + } + else if (modified) + { + 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. + */ + if (last_entry + || (!since_date && !*since_rev && !*since_tag && !*backto)) + { + repos_sort++; + file_sort++; + /* + * If we are not looking for last_modified and the user specified + * one or more users to look at, sort by user before filename. + */ + if (!last_entry && user_list) + user_sort++; + } + } + else if (module_report) + { + free (rec_types); + rec_types = xstrdup (last_entry ? "OMAR" : ALL_HISTORY_REC_TYPES); + module_sort++; + repos_sort++; + file_sort++; + working = 0; /* User's workdir doesn't count here */ + } + else + /* Must be "checkout" or default */ + { + free (rec_types); + rec_types = xstrdup ("OF"); + /* See comments in "modified" above */ + if (!last_entry && user_list) + user_sort++; + if (last_entry + || (!since_date && !*since_rev && !*since_tag && !*backto)) + file_sort++; + } + + /* If no users were specified, use self (-a saves a universal ("") user) */ + if (!user_list) + save_user (getcaller ()); + + /* 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"); + } + + if (histfile) + fname = xstrdup (histfile); + else + { + fname = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_HISTORY) + 10); + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_HISTORY); + } + + read_hrecs (fname); + if(hrec_count>0) + { + qsort ((void *) hrec_head, hrec_count, + sizeof (struct hrec), sort_order); + } + report_hrecs (); + free (fname); + if (since_date != NULL) + free (since_date); + free (since_rev); + free (since_tag); + free (backto); + free (rec_types); + + return 0; +} + + + +void +history_write (int type, const char *update_dir, const char *revs, + const char *name, const char *repository) +{ + char *fname; + char *workdir; + char *username = getcaller (); + int fd; + char *line; + char *slash = "", *cp; + const char *cp2, *repos; + int i; + static char *tilde = ""; + static char *PrCurDir = NULL; + + if (logoff) /* History is turned off by noexec or + * readonlyfs. + */ + return; + if ( strchr(logHistory, type) == NULL ) + return; + fname = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_HISTORY) + 3); + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_HISTORY); + + /* turn off history logging if the history file does not exist */ + /* FIXME: This should check for write permissions instead. This way, + * O_CREATE could be added back into the call to open() below and + * there would be no race condition involved in log rotation. + * + * Note that the new method of turning off logging would be either via + * the CVSROOT/config file (probably the quicker method, but would need + * to be added, or at least checked for, too) or by creating a dummy + * history file with 0444 permissions. + */ + if (!isfile (fname)) + { + logoff = 1; + goto out; + } + + TRACE ( 1, "fopen(%s,a)", fname ); + + if (noexec) + goto out; + fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | OPEN_BINARY, 0666); + if (fd < 0) + { + if (! really_quiet) + { + error (0, errno, "warning: cannot write to history file %s", + fname); + } + goto out; + } + + repos = Short_Repository (repository); + + if (!PrCurDir) + { + char *pwdir; + + pwdir = get_homedir (); + PrCurDir = CurDir; + if (pwdir != NULL) + { + /* Assumes neither CurDir nor pwdir ends in '/' */ + i = strlen (pwdir); + if (!strncmp (CurDir, pwdir, i)) + { + PrCurDir += i; /* Point to '/' separator */ + tilde = "~"; + } + else + { + /* Try harder to find a "homedir" */ + struct saved_cwd cwd; + char *homedir; + + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + + if ( CVS_CHDIR (pwdir) < 0 || (homedir = xgetwd ()) == NULL) + homedir = pwdir; + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + + i = strlen (homedir); + if (!strncmp (CurDir, homedir, i)) + { + PrCurDir += i; /* Point to '/' separator */ + tilde = "~"; + } + + if (homedir != pwdir) + free (homedir); + } + } + } + + if (type == 'T') + { + repos = update_dir; + update_dir = ""; + } + else if (update_dir && *update_dir) + slash = "/"; + 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); + + /* + * "workdir" is the directory where the file "name" is. ("^~" == $HOME) + * "repos" is the Repository, relative to $CVSROOT where the RCS file is. + * + * "$workdir/$name" is the working file name. + * "$CVSROOT/$repos/$name,v" is the RCS file in the Repository. + * + * First, note that the history format was intended to save space, not + * to be human readable. + * + * The working file directory ("workdir") and the Repository ("repos") + * usually end with the same one or more directory elements. To avoid + * duplication (and save space), the "workdir" field ends with + * an integer offset into the "repos" field. This offset indicates the + * beginning of the "tail" of "repos", after which all characters are + * duplicates. + * + * In other words, if the "workdir" field has a '*' (a very stupid thing + * to put in a filename) in it, then every thing following the last '*' + * is a hex offset into "repos" of the first character from "repos" to + * append to "workdir" to finish the pathname. + * + * It might be easier to look at an example: + * + * M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo + * + * Indicates that the workdir is really "~/work/cvs/examples", saving + * 10 characters, where "~/work*d" would save 6 characters and mean that + * the workdir is really "~/work/examples". It will mean more on + * directories like: usr/local/gnu/emacs/dist-19.17/lisp/term + * + * "workdir" is always an absolute pathname (~/xxx is an absolute path) + * "repos" is always a relative pathname. So we can assume that we will + * never run into the top of "workdir" -- there will always be a '/' or + * a '~' at the head of "workdir" that is not matched by anything in + * "repos". On the other hand, we *can* run off the top of "repos". + * + * Only "compress" if we save characters. + */ + + if (!repos) + repos = ""; + + cp = workdir + strlen (workdir) - 1; + cp2 = repos + strlen (repos) - 1; + for (i = 0; cp2 >= repos && cp > workdir && *cp == *cp2--; cp--) + i++; + + if (i > 2) + { + i = strlen (repos) - i; + (void) sprintf ((cp + 1), "*%x", i); + } + + if (!revs) + revs = ""; + line = xmalloc (strlen (username) + strlen (workdir) + strlen (repos) + + strlen (revs) + strlen (name) + 100); + sprintf (line, "%c%08lx|%s|%s|%s|%s|%s\n", + type, (long) time ((time_t *) NULL), + username, workdir, repos, revs, name); + + /* Lessen some race conditions on non-Posix-compliant hosts. */ + if (lseek (fd, (off_t) 0, SEEK_END) == -1) + error (1, errno, "cannot seek to end of history file: %s", fname); + + if (write (fd, line, strlen (line)) < 0) + error (1, errno, "cannot write to history file: %s", fname); + free (line); + if (close (fd) != 0) + error (1, errno, "cannot close history file: %s", fname); + free (workdir); + out: + free (fname); +} + +/* + * save_user() adds a user name to the user list to select. Zero-length + * username ("") matches any user. + */ +static void +save_user (char *name) +{ + if (user_count == user_max) + { + user_max = xsum (user_max, USER_INCREMENT); + if (size_overflow_p (xtimes (user_max, sizeof (char *)))) + { + error (0, 0, "save_user: too many users"); + return; + } + user_list = xrealloc (user_list, xtimes (user_max, sizeof (char *))); + } + user_list[user_count++] = xstrdup (name); +} + +/* + * save_file() adds file name and associated module to the file list to select. + * + * If "dir" is null, store a file name as is. + * If "name" is null, store a directory name with a '*' on the front. + * Else, store concatenated "dir/name". + * + * Later, in the "select" stage: + * - if it starts with '*', it is prefix-matched against the repository. + * - if it has a '/' in it, it is matched against the repository/file. + * - else it is matched against the file name. + */ +static void +save_file (char *dir, char *name, char *module) +{ + char *cp; + struct file_list_str *fl; + + if (file_count == file_max) + { + file_max = xsum (file_max, FILE_INCREMENT); + if (size_overflow_p (xtimes (file_max, sizeof (*fl)))) + { + error (0, 0, "save_file: too many files"); + return; + } + file_list = xrealloc (file_list, xtimes (file_max, sizeof (*fl))); + } + fl = &file_list[file_count++]; + fl->l_file = cp = xmalloc (strlen (dir) + strlen (name) + 2); + fl->l_module = module; + + if (dir && *dir) + { + if (name && *name) + { + (void) strcpy (cp, dir); + (void) strcat (cp, "/"); + (void) strcat (cp, name); + } + else + { + *cp++ = '*'; + (void) strcpy (cp, dir); + } + } + else + { + if (name && *name) + { + (void) strcpy (cp, name); + } + else + { + error (0, 0, "save_file: null dir and file name"); + } + } +} + +static void +save_module (char *module) +{ + if (mod_count == mod_max) + { + mod_max = xsum (mod_max, MODULE_INCREMENT); + if (size_overflow_p (xtimes (mod_max, sizeof (char *)))) + { + error (0, 0, "save_module: too many modules"); + return; + } + mod_list = xrealloc (mod_list, xtimes (mod_max, sizeof (char *))); + } + mod_list[mod_count++] = xstrdup (module); +} + +static void +expand_modules (void) +{ +} + +/* fill_hrec + * + * Take a ptr to 7-part history line, ending with a newline, for example: + * + * M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo + * + * Split it into 7 parts and drop the parts into a "struct hrec". + * Return a pointer to the character following the newline. + * + */ + +#define NEXT_BAR(here) do { \ + while (isspace(*line)) line++; \ + hr->here = line; \ + while ((c = *line++) && c != '|') ; \ + if (!c) return; line[-1] = '\0'; \ + } while (0) + +static void +fill_hrec (char *line, struct hrec *hr) +{ + char *cp; + int c; + + hr->type = hr->user = hr->dir = hr->repos = hr->rev = hr->file = + hr->end = hr->mod = NULL; + hr->date = -1; + hr->idx = ++hrec_idx; + + while (isspace ((unsigned char) *line)) + line++; + + hr->type = line++; + hr->date = strtoul (line, &cp, 16); + if (cp == line || *cp != '|') + return; + line = cp + 1; + NEXT_BAR (user); + NEXT_BAR (dir); + if ((cp = strrchr (hr->dir, '*')) != NULL) + { + *cp++ = '\0'; + hr->end = line + strtoul (cp, NULL, 16); + } + else + hr->end = line - 1; /* A handy pointer to '\0' */ + NEXT_BAR (repos); + NEXT_BAR (rev); + if (strchr ("FOET", *(hr->type))) + hr->mod = line; + + NEXT_BAR (file); +} + + +#ifndef STAT_BLOCKSIZE +#if HAVE_STRUCT_STAT_ST_BLKSIZE +#define STAT_BLOCKSIZE(s) (s).st_blksize +#else +#define STAT_BLOCKSIZE(s) (4 * 1024) +#endif +#endif + + +/* read_hrecs's job is to read the history file and fill in all the "hrec" + * (history record) array elements with the ones we need to print. + * + * Logic: + * - Read a block from the file. + * - Walk through the block parsing line into hr records. + * - if the hr isn't used, free its strings, if it is, bump the hrec counter + * - at the end of a block, copy the end of the current block to the start + * of space for the next block, then read in the next block. If we get less + * than the whole block, we're done. + */ +static void +read_hrecs (char *fname) +{ + unsigned char *cpstart, *cpend, *cp, *nl; + char *hrline; + int i; + int fd; + struct stat st_buf; + + 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) + error (1, errno, "can't stat history file"); + + if (!(st_buf.st_size)) + error (1, 0, "history file is empty"); + + cpstart = xmalloc (2 * STAT_BLOCKSIZE(st_buf)); + cpstart[0] = '\0'; + cp = cpend = cpstart; + + hrec_max = HREC_INCREMENT; + hrec_head = xmalloc (hrec_max * sizeof (struct hrec)); + hrec_idx = 0; + + for (;;) + { + for (nl = cp; nl < cpend && *nl != '\n'; nl++) + if (!isprint(*nl)) *nl = ' '; + + if (nl >= cpend) + { + if (nl - cp >= STAT_BLOCKSIZE(st_buf)) + { + error(1, 0, "history line %ld too long (> %lu)", hrec_idx + 1, + (unsigned long) STAT_BLOCKSIZE(st_buf)); + } + if (nl > cp) + memmove (cpstart, cp, nl - cp); + nl = cpstart + (nl - cp); + cp = cpstart; + i = read (fd, nl, STAT_BLOCKSIZE(st_buf)); + if (i > 0) + { + cpend = nl + i; + *cpend = '\0'; + continue; + } + if (i < 0) + error (1, errno, "error reading history file"); + if (nl == cp) break; + error (0, 0, "warning: no newline at end of history file"); + } + *nl = '\0'; + + if (hrec_count == hrec_max) + { + struct hrec *old_head = hrec_head; + + hrec_max += HREC_INCREMENT; + hrec_head = xrealloc ((char *) hrec_head, + hrec_max * sizeof (struct hrec)); + if (last_since_tag) + last_since_tag = hrec_head + (last_since_tag - old_head); + if (last_backto) + last_backto = hrec_head + (last_backto - old_head); + } + + /* fill_hrec dates from when history read the entire + history file in one chunk, and then records were pulled out + by pointing to the various parts of this big chunk. This is + why there are ugly hacks here: I don't want to completely + re-write the whole history stuff right now. */ + + hrline = xstrdup ((char *)cp); + fill_hrec (hrline, &hrec_head[hrec_count]); + if (select_hrec (&hrec_head[hrec_count])) + hrec_count++; + else + free(hrline); + + cp = nl + 1; + } + free (cpstart); + close (fd); + + /* Special selection problem: If "since_tag" is set, we have saved every + * record from the 1st occurrence of "since_tag", when we want to save + * records since the *last* occurrence of "since_tag". So what we have + * to do is bump hrec_head forward and reduce hrec_count accordingly. + */ + if (last_since_tag) + { + hrec_count -= (last_since_tag - hrec_head); + hrec_head = last_since_tag; + } + + /* Much the same thing is necessary for the "backto" option. */ + if (last_backto) + { + hrec_count -= (last_backto - hrec_head); + hrec_head = last_backto; + } +} + +/* Utility program for determining whether "find" is inside "string" */ +static int +within (char *find, char *string) +{ + int c, len; + + if (!find || !string) + return (0); + + c = *find++; + len = strlen (find); + + while (*string) + { + if (!(string = strchr (string, c))) + return (0); + string++; + if (!strncmp (find, string, len)) + return (1); + } + return (0); +} + +/* The purpose of "select_hrec" is to apply the selection criteria based on + * the command arguments and defaults and return a flag indicating whether + * this record should be remembered for printing. + */ +static int +select_hrec (struct hrec *hr) +{ + char **cpp, *cp, *cp2; + struct file_list_str *fl; + int count; + + /* basic validity checking */ + if (!hr->type || !hr->user || !hr->dir || !hr->repos || !hr->rev || + !hr->file || !hr->end) + { + error (0, 0, "warning: history line %ld invalid", hr->idx); + return (0); + } + + /* "Since" checking: The argument parser guarantees that only one of the + * following four choices is set: + * + * 1. If "since_date" is set, it contains the date specified on the + * command line. hr->date fields earlier than "since_date" are ignored. + * 2. If "since_rev" is set, it contains either an RCS "dotted" revision + * number (which is of limited use) or a symbolic TAG. Each RCS file + * is examined and the date on the specified revision (or the revision + * corresponding to the TAG) in the RCS file (CVSROOT/repos/file) is + * compared against hr->date as in 1. above. + * 3. If "since_tag" is set, matching tag records are saved. The field + * "last_since_tag" is set to the last one of these. Since we don't + * know where the last one will be, all records are saved from the + * first occurrence of the TAG. Later, at the end of "select_hrec" + * records before the last occurrence of "since_tag" are skipped. + * 4. If "backto" is set, all records with a module name or file name + * matching "backto" are saved. In addition, all records with a + * repository field with a *prefix* matching "backto" are saved. + * The field "last_backto" is set to the last one of these. As in + * 3. above, "select_hrec" adjusts to include the last one later on. + */ + if (since_date) + { + char *ourdate = date_from_time_t (hr->date); + count = RCS_datecmp (ourdate, since_date); + free (ourdate); + if (count < 0) + return (0); + } + else if (*since_rev) + { + Vers_TS *vers; + time_t t; + 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)) + != (time_t) 0) + { + if (hr->date < t) + { + freevers_ts (&vers); + return (0); + } + } + } + freevers_ts (&vers); + } + else if (*since_tag) + { + if (*(hr->type) == 'T') + { + /* + * A 'T'ag record, the "rev" field holds the tag to be set, + * while the "repos" field holds "D"elete, "A"dd or a rev. + */ + if (within (since_tag, hr->rev)) + { + last_since_tag = hr; + return (1); + } + else + return (0); + } + if (!last_since_tag) + return (0); + } + else if (*backto) + { + if (within (backto, hr->file) || within (backto, hr->mod) || + within (backto, hr->repos)) + last_backto = hr; + else + return (0); + } + + /* User checking: + * + * Run down "user_list", match username ("" matches anything) + * If "" is not there and actual username is not there, return failure. + */ + if (user_list && hr->user) + { + for (cpp = user_list, count = user_count; count; cpp++, count--) + { + if (!**cpp) + break; /* null user == accept */ + if (!strcmp (hr->user, *cpp)) /* found listed user */ + break; + } + if (!count) + return (0); /* Not this user */ + } + + /* Record type checking: + * + * 1. If Record type is not in rec_types field, skip it. + * 2. If mod_list is null, keep everything. Otherwise keep only modules + * on mod_list. + * 3. If neither a 'T', 'F' nor 'O' record, run through "file_list". If + * file_list is null, keep everything. Otherwise, keep only files on + * file_list, matched appropriately. + */ + if (!strchr (rec_types, *(hr->type))) + return (0); + if (!strchr ("TFOE", *(hr->type))) /* Don't bother with "file" if "TFOE" */ + { + if (file_list) /* If file_list is null, accept all */ + { + for (fl = file_list, count = file_count; count; fl++, count--) + { + /* 1. If file_list entry starts with '*', skip the '*' and + * compare it against the repository in the hrec. + * 2. If file_list entry has a '/' in it, compare it against + * the concatenation of the repository and file from hrec. + * 3. Else compare the file_list entry against the hrec file. + */ + char *cmpfile = NULL; + + if (*(cp = fl->l_file) == '*') + { + cp++; + /* if argument to -p is a prefix of repository */ + if (!strncmp (cp, hr->repos, strlen (cp))) + { + hr->mod = fl->l_module; + break; + } + } + else + { + if (strchr (cp, '/')) + { + cmpfile = xmalloc (strlen (hr->repos) + + strlen (hr->file) + + 10); + (void) sprintf (cmpfile, "%s/%s", + hr->repos, hr->file); + cp2 = cmpfile; + } + else + { + cp2 = hr->file; + } + + /* if requested file is found within {repos}/file fields */ + if (within (cp, cp2)) + { + hr->mod = fl->l_module; + break; + } + if (cmpfile != NULL) + free (cmpfile); + } + } + if (!count) + return (0); /* String specified and no match */ + } + } + if (mod_list) + { + for (cpp = mod_list, count = mod_count; count; cpp++, count--) + { + if (hr->mod && !strcmp (hr->mod, *cpp)) /* found module */ + break; + } + if (!count) + return (0); /* Module specified & this record is not one of them. */ + } + + return (1); /* Select this record unless rejected above. */ +} + +/* The "sort_order" routine (when handed to qsort) has arranged for the + * hrecs files to be in the right order for the report. + * + * Most of the "selections" are done in the select_hrec routine, but some + * selections are more easily done after the qsort by "accept_hrec". + */ +static void +report_hrecs (void) +{ + struct hrec *hr, *lr; + struct tm *tm; + int i, count, ty; + char *cp; + int user_len, file_len, rev_len, mod_len, repos_len; + + if (*since_tag && !last_since_tag) + { + (void) printf ("No tag found: %s\n", since_tag); + return; + } + else if (*backto && !last_backto) + { + (void) printf ("No module, file or repository with: %s\n", backto); + return; + } + else if (hrec_count < 1) + { + (void) printf ("No records selected.\n"); + return; + } + + user_len = file_len = rev_len = mod_len = repos_len = 0; + + /* Run through lists and find maximum field widths */ + hr = lr = hrec_head; + hr++; + for (count = hrec_count; count--; lr = hr, hr++) + { + char *repos; + + if (!count) + hr = NULL; + if (!accept_hrec (lr, hr)) + continue; + + ty = *(lr->type); + repos = xstrdup (lr->repos); + if ((cp = strrchr (repos, '/')) != NULL) + { + if (lr->mod && !strcmp (++cp, lr->mod)) + { + (void) strcpy (cp, "*"); + } + } + if ((i = strlen (lr->user)) > user_len) + user_len = i; + if ((i = strlen (lr->file)) > file_len) + file_len = i; + if (ty != 'T' && (i = strlen (repos)) > repos_len) + repos_len = i; + if (ty != 'T' && (i = strlen (lr->rev)) > rev_len) + 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. + * "hr" points to the record following "lr" -- It is NULL in the last + * pass. + * + * There are two sections in the loop below: + * 1. Based on the report type (e.g. extract, checkout, tag, etc.), + * decide whether the record should be printed. + * 2. Based on the record type, format and print the data. + */ + for (lr = hrec_head, hr = (lr + 1); hrec_count--; lr = hr, hr++) + { + char *workdir; + char *repos; + + if (!hrec_count) + hr = NULL; + if (!accept_hrec (lr, hr)) + continue; + + ty = *(lr->type); + if (!tz_local) + { + time_t t = lr->date + tz_seconds_east_of_GMT; + tm = gmtime (&t); + } + else + tm = localtime (&(lr->date)); + + (void) printf ("%c %04d-%02d-%02d %02d:%02d %s %-*s", ty, + tm->tm_year+1900, tm->tm_mon + 1, 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) + { + if (lr->mod && !strcmp (++cp, lr->mod)) + { + (void) strcpy (cp, "*"); + } + } + repos = xmalloc (strlen (lr->repos) + 10); + (void) strcpy (repos, lr->repos); + if ((cp = strrchr (repos, '/')) != NULL) + { + if (lr->mod && !strcmp (++cp, lr->mod)) + { + (void) strcpy (cp, "*"); + } + } + + switch (ty) + { + case 'T': + /* 'T'ag records: repository is a "tag type", rev is the tag */ + (void) printf (" %-*s [%s:%s]", mod_len, lr->mod, lr->rev, + repos); + if (working) + (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 - (int) strlen (lr->mod), + "=", workdir); + break; + case 'W': + case 'U': + case 'P': + case 'C': + case 'G': + case 'M': + case 'A': + case 'R': + (void) printf (" %-*s %-*s %-*s =%s= %s", rev_len, lr->rev, + file_len, lr->file, repos_len, repos, + lr->mod ? lr->mod : "", workdir); + break; + default: + (void) printf ("Hey! What is this junk? RecType[0x%2.2x]", ty); + break; + } + (void) putchar ('\n'); + free (workdir); + free (repos); + } +} + +static int +accept_hrec (struct hrec *lr, struct hrec *hr) +{ + int ty; + + ty = *(lr->type); + + if (last_since_tag && ty == 'T') + return (1); + + if (v_checkout) + { + if (ty != 'O') + return (0); /* Only interested in 'O' records */ + + /* We want to identify all the states that cause the next record + * ("hr") to be different from the current one ("lr") and only + * print a line at the allowed boundaries. + */ + + if (!hr || /* The last record */ + strcmp (hr->user, lr->user) || /* User has changed */ + strcmp (hr->mod, lr->mod) ||/* Module has changed */ + (working && /* If must match "workdir" */ + (strcmp (hr->dir, lr->dir) || /* and the 1st parts or */ + strcmp (hr->end, lr->end)))) /* the 2nd parts differ */ + + return (1); + } + else if (modified) + { + if (!last_entry || /* Don't want only last rec */ + !hr || /* Last entry is a "last entry" */ + strcmp (hr->repos, lr->repos) || /* Repository has changed */ + strcmp (hr->file, lr->file))/* File has changed */ + return (1); + + if (working) + { /* If must match "workdir" */ + if (strcmp (hr->dir, lr->dir) || /* and the 1st parts or */ + strcmp (hr->end, lr->end)) /* the 2nd parts differ */ + return (1); + } + } + else if (module_report) + { + if (!last_entry || /* Don't want only last rec */ + !hr || /* Last entry is a "last entry" */ + strcmp (hr->mod, lr->mod) ||/* Module has changed */ + strcmp (hr->repos, lr->repos) || /* Repository has changed */ + strcmp (hr->file, lr->file))/* File has changed */ + return (1); + } + else + { + /* "extract" and "tag_report" always print selected records. */ + return (1); + } + + return (0); +} diff --git a/contrib/cvs-1.12.9/src/history.h b/contrib/cvs-1.12.9/src/history.h new file mode 100644 index 0000000000..fd65951c65 --- /dev/null +++ b/contrib/cvs-1.12.9/src/history.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2003, Derek Price, Ximbiot , + * and the Free Software Foundation + * + * You may distribute under the terms of the GNU General Public License + * as specified in the README file that comes with the CVS source + * distribution. + * + * This is the header file for definitions and functions shared by history.c + * with other portions of CVS. + */ + +#define ALL_HISTORY_REC_TYPES "TOEFWUPCGMAR" diff --git a/contrib/cvs-1.12.9/src/ignore.c b/contrib/cvs-1.12.9/src/ignore.c new file mode 100644 index 0000000000..e24d98b5ef --- /dev/null +++ b/contrib/cvs-1.12.9/src/ignore.c @@ -0,0 +1,480 @@ +/* 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 + */ + +#include "cvs.h" +#include "getline.h" + +/* + * Ignore file section. + * + * "!" may be included any time to reset the list (i.e. ignore nothing); + * "*" may be specified to ignore everything. It stays as the first + * element forever, unless a "!" clears it out. + */ + +static char **ign_list; /* List of files to ignore in update + * and import */ +static char **s_ign_list = NULL; +static int ign_count; /* Number of active entries */ +static int s_ign_count = 0; +static int ign_size; /* This many slots available (plus + * one for a NULL) */ +static int ign_hold = -1; /* Index where first "temporary" item + * is held */ + +const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\ + .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj\ + *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$"; + +#define IGN_GROW 16 /* grow the list by 16 elements at a + * time */ + +/* Nonzero if we have encountered an -I ! directive, which means one should + no longer ask the server about what is in CVSROOTADM_IGNORE. */ +int ign_inhibit_server; + + + +/* + * To the "ignore list", add the hard-coded default ignored wildcards above, + * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in + * ~/.cvsignore and the wildcards found in the CVSIGNORE environment + * variable. + */ +void +ign_setup (void) +{ + char *home_dir; + char *tmp; + + ign_inhibit_server = 0; + + /* Start with default list and special case */ + tmp = xstrdup (ign_default); + ign_add (tmp, 0); + free (tmp); + +#ifdef CLIENT_SUPPORT + /* The client handles another way, by (after it does its own ignore file + processing, and only if !ign_inhibit_server), letting the server + know about the files and letting it decide whether to ignore + them based on CVSROOOTADM_IGNORE. */ + if (!current_parsed_root->isremote) +#endif + { + char *file = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_IGNORE) + 10); + /* Then add entries found in repository, if it exists */ + (void) sprintf (file, "%s/%s/%s", current_parsed_root->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 */ + home_dir = get_homedir (); + /* If we can't find a home directory, ignore ~/.cvsignore. This may + make tracking down problems a bit of a pain, but on the other + hand it might be obnoxious to complain when CVS will function + just fine without .cvsignore (and many users won't even know what + .cvsignore is). */ + if (home_dir) + { + char *file = strcat_filename_onto_homedir (home_dir, CVSDOTIGNORE); + ign_add_file (file, 0); + free (file); + } + + /* Then add entries found in CVSIGNORE environment variable. */ + ign_add (getenv (IGNORE_ENV), 0); + + /* Later, add ignore entries found in -I arguments */ +} + + + +/* + * Open a file and read lines, feeding each line to a line parser. Arrange + * for keeping a temporary list of wildcards at the end, if the "hold" + * argument is set. + */ +void +ign_add_file (char *file, int hold) +{ + FILE *fp; + char *line = NULL; + size_t line_allocated = 0; + + /* restore the saved list (if any) */ + if (s_ign_list != NULL) + { + int i; + + for (i = 0; i < s_ign_count; i++) + ign_list[i] = s_ign_list[i]; + ign_count = s_ign_count; + ign_list[ign_count] = NULL; + + s_ign_count = 0; + free (s_ign_list); + s_ign_list = NULL; + } + + /* is this a temporary ignore file? */ + if (hold) + { + /* re-set if we had already done a temporary file */ + if (ign_hold >= 0) + { + int i; + + for (i = ign_hold; i < ign_count; i++) + free (ign_list[i]); + ign_count = ign_hold; + ign_list[ign_count] = NULL; + } + else + { + ign_hold = ign_count; + } + } + + /* load the file */ + fp = CVS_FOPEN (file, "r"); + if (fp == NULL) + { + if (! existence_error (errno)) + error (0, errno, "cannot open %s", file); + return; + } + 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. */ +void +ign_add (char *ign, int hold) +{ + if (!ign || !*ign) + return; + + for (; *ign; ign++) + { + char *mark; + char save; + + /* ignore whitespace before the token */ + if (isspace ((unsigned char) *ign)) + continue; + + /* If we have used up all the space, add some more. Do this before + processing `!', since an "empty" list still contains the `CVS' + entry. */ + if (ign_count >= ign_size) + { + ign_size += IGN_GROW; + ign_list = (char **) xrealloc ((char *) ign_list, + (ign_size + 1) * sizeof (char *)); + } + + /* + * if we find a single character !, we must re-set the ignore list + * (saving it if necessary). We also catch * as a special case in a + * global ignore file as an optimization + */ + if ((!*(ign+1) || isspace ((unsigned char) *(ign+1))) + && (*ign == '!' || *ign == '*')) + { + if (!hold) + { + /* permanently reset the ignore list */ + int i; + + for (i = 0; i < ign_count; i++) + free (ign_list[i]); + ign_count = 1; + /* Always ignore the "CVS" directory. */ + ign_list[0] = xstrdup("CVS"); + ign_list[1] = NULL; + + /* if we are doing a '!', continue; otherwise add the '*' */ + if (*ign == '!') + { + ign_inhibit_server = 1; + continue; + } + } + else if (*ign == '!') + { + /* temporarily reset the ignore list */ + int i; + + if (ign_hold >= 0) + { + for (i = ign_hold; i < ign_count; i++) + free (ign_list[i]); + ign_hold = -1; + } + s_ign_list = (char **) xmalloc (ign_count * sizeof (char *)); + for (i = 0; i < ign_count; i++) + s_ign_list[i] = ign_list[i]; + s_ign_count = ign_count; + ign_count = 1; + /* Always ignore the "CVS" directory. */ + ign_list[0] = xstrdup ("CVS"); + ign_list[1] = NULL; + continue; + } + } + + /* find the end of this token */ + for (mark = ign; *mark && !isspace ((unsigned char) *mark); mark++) + /* do nothing */ ; + + save = *mark; + *mark = '\0'; + + ign_list[ign_count++] = xstrdup (ign); + ign_list[ign_count] = NULL; + + *mark = save; + if (save) + ign = mark; + else + ign = mark - 1; + } +} + + + +/* Return true if the given filename should be ignored by update or import, + * else return false. + */ +int +ign_name (char *name) +{ + char **cpp = ign_list; + + if (cpp == NULL) + return 0; + + while (*cpp) + if (CVS_FNMATCH (*cpp++, name, 0) == 0) + return 1; + + return 0; +} + + + +/* FIXME: This list of dirs to ignore stuff seems not to be used. + Really? send_dirent_proc and update_dirent_proc both call + ignore_directory and do_module calls ign_dir_add. No doubt could + use some documentation/testsuite work. */ + +static char **dir_ign_list = NULL; +static int dir_ign_max = 0; +static int dir_ign_current = 0; + +/* Add a directory to list of dirs to ignore. */ +void +ign_dir_add (char *name) +{ + /* Make sure we've got the space for the entry. */ + if (dir_ign_current <= dir_ign_max) + { + dir_ign_max += IGN_GROW; + dir_ign_list = + (char **) xrealloc (dir_ign_list, + (dir_ign_max + 1) * sizeof (char *)); + } + + dir_ign_list[dir_ign_current++] = xstrdup (name); +} + + +/* Return nonzero if NAME is part of the list of directories to ignore. */ + +int +ignore_directory (const char *name) +{ + int i; + + if (!dir_ign_list) + return 0; + + i = dir_ign_current; + while (i--) + { + if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])) == 0) + return 1; + } + + return 0; +} + + + +/* + * 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 (List *ilist, List *entries, const char *update_dir, + Ignore_proc proc) +{ + int subdirs; + DIR *dirp; + struct dirent *dp; + struct stat sb; + char *file; + const char *xdir; + List *files; + Node *p; + + /* Set SUBDIRS if we have subdirectory information in ENTRIES. */ + if (entries == NULL) + subdirs = 0; + else + { + struct stickydirtag *sdtp = 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 = CVS_OPENDIR ("."); + if (dirp == NULL) + { + error (0, errno, "cannot open current directory"); + return; + } + + ign_add_file (CVSDOTIGNORE, 1); + wrap_add_file (CVSDOTWRAPPER, 1); + + /* Make a list for the files. */ + files = getlist (); + + while (errno = 0, (dp = CVS_READDIR (dirp)) != NULL) + { + file = dp->d_name; + if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0) + 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 + dp->d_type != DT_UNKNOWN || +#endif + CVS_LSTAT( file, &sb ) != -1 ) + { + + if ( +#ifdef DT_DIR + dp->d_type == DT_DIR + || (dp->d_type == DT_UNKNOWN && S_ISDIR (sb.st_mode)) +#else + S_ISDIR (sb.st_mode) +#endif + ) + { + 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 ( +#ifdef DT_DIR + dp->d_type == DT_LNK + || (dp->d_type == DT_UNKNOWN && S_ISLNK(sb.st_mode)) +#else + S_ISLNK (sb.st_mode) +#endif + ) + { + continue; + } +#endif + } + + p = getnode (); + p->type = FILES; + p->key = xstrdup (file); + (void) addnode (files, p); + } + if (errno != 0) + error (0, errno, "error reading current directory"); + (void) CVS_CLOSEDIR (dirp); + + sortlist (files, fsortcmp); + for (p = files->list->next; p != files->list; p = p->next) + (*proc) (p->key, xdir); + dellist (&files); +} diff --git a/contrib/cvs-1.12.9/src/import.c b/contrib/cvs-1.12.9/src/import.c new file mode 100644 index 0000000000..7c72e2d250 --- /dev/null +++ b/contrib/cvs-1.12.9/src/import.c @@ -0,0 +1,1603 @@ +/* + * 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 source distribution. + * + * "import" checks in the vendor release located in the current directory into + * the CVS source repository. The CVS vendor branch support is utilized. + * + * At least three arguments are expected to follow the options: + * repository Where the source belongs relative to the CVSROOT + * VendorTag Vendor's major tag + * VendorReleTag Tag for this particular release + * + * Additional arguments specify more Vendor Release Tags. + */ + +#include "cvs.h" +#include "savecwd.h" + +static char *get_comment (const char *user); +static int add_rev (char *message, RCSNode *rcs, char *vfile, + char *vers); +static int add_tags (RCSNode *rcs, char *vfile, char *vtag, int targc, + char *targv[]); +static int import_descend (char *message, char *vtag, int targc, char *targv[]); +static int import_descend_dir (char *message, char *dir, char *vtag, + int targc, char *targv[]); +static int process_import_file (char *message, char *vfile, char *vtag, + int targc, char *targv[]); +static int update_rcs_file (char *message, char *vfile, char *vtag, int targc, + char *targv[], int inattic); +static void add_log (int ch, char *fname); + +static int repos_len; +static char *vhead; +static char *vbranch; +static FILE *logfp; +static char *repository; +static int conflicts; +static int use_file_modtime; +static char *keyword_opt = NULL; + +static const char *const import_usage[] = +{ + "Usage: %s %s [-d] [-k subst] [-I ign] [-m msg] [-b branch]\n", + " [-W spec] repository vendor-tag release-tags...\n", + "\t-d\tUse the file's modification time as the time of import.\n", + "\t-k sub\tSet default RCS keyword substitution mode.\n", + "\t-I ign\tMore files to ignore (! to reset).\n", + "\t-b bra\tVendor branch id.\n", + "\t-m msg\tLog message.\n", + "\t-W spec\tWrappers specification line.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +import (int argc, char **argv) +{ + char *message = NULL; + char *tmpfile; + char *cp; + int i, c, msglen, err; + List *ulist; + Node *p; + struct logfile_info *li; + + if (argc == -1) + usage (import_usage); + + ign_setup (); + wrap_setup (); + + vbranch = xstrdup (CVSBRANCH); + optind = 0; + while ((c = getopt (argc, argv, "+Qqdb:m:I:k:W:")) != -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) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + cvs_cmd_name); + break; + case 'd': +#ifdef SERVER_SUPPORT + if (server_active) + { + /* CVS 1.10 and older clients will send this, but it + doesn't do any good. So tell the user we can't + cope, rather than silently losing. */ + error (0, 0, + "warning: not setting the time of import from the file"); + error (0, 0, "due to client limitations"); + } +#endif + use_file_modtime = 1; + break; + case 'b': + free (vbranch); + vbranch = xstrdup (optarg); + break; + case 'm': +#ifdef FORCE_USE_EDITOR + use_editor = 1; +#else + use_editor = 0; +#endif + message = xstrdup(optarg); + break; + case 'I': + ign_add (optarg, 0); + break; + case 'k': + /* 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)); + keyword_opt = optarg; + break; + case 'W': + wrap_add (optarg, 0); + break; + case '?': + default: + usage (import_usage); + break; + } + } + argc -= optind; + argv += optind; + if (argc < 3) + usage (import_usage); + +#ifdef SERVER_SUPPORT + /* This is for handling the Checkin-time request. It might seem a + bit odd to enable the use_file_modtime code even in the case + where Checkin-time was not sent for a particular file. The + effect is that we use the time of upload, rather than the time + when we call RCS_checkin. Since those times are both during + CVS's run, that seems OK, and it is easier to implement than + putting the "was Checkin-time sent" flag in CVS/Entries or some + such place. */ + + if (server_active) + use_file_modtime = 1; +#endif + + /* Don't allow "CVS" as any directory in module path. + * + * Could abstract this to valid_module_path, but I don't think we'll need + * to call it from anywhere else. + */ + if ((cp = strstr(argv[0], "CVS")) && /* path contains "CVS" AND ... */ + ((cp == argv[0]) || ISSLASH(*(cp-1))) && /* /^CVS/ OR m#/CVS# AND ... */ + ((*(cp+3) == '\0') || ISSLASH(*(cp+3))) /* /CVS$/ OR m#CVS/# */ + ) + { + error (0, 0, + "The word `CVS' is reserved by CVS and may not be used"); + error (1, 0, "as a directory in a path or as a file name."); + } + + 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]) && pathname_levels (argv[0]) == 0) + { + if (current_parsed_root == NULL) + { + error (0, 0, "missing CVSROOT environment variable\n"); + error (1, 0, "Set it or specify the '-d' option to %s.", + program_name); + } + repository = xmalloc (strlen (current_parsed_root->directory) + + strlen (argv[0]) + + 2); + (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); + repos_len = strlen (current_parsed_root->directory); + } + else + { + /* It is somewhere between a security hole and "unexpected" to + let the client start mucking around outside the cvsroot + (wouldn't get the right CVSROOT configuration, &c). */ + error (1, 0, "directory %s not relative within the repository", + argv[0]); + } + + /* + * Consistency checks on the specified vendor branch. It must be + * composed of only numbers and dots ('.'). Also, for now we only + * support branching to a single level, so the specified vendor branch + * must only have two dots in it (like "1.1.1"). + */ + for (cp = vbranch; *cp != '\0'; cp++) + if (!isdigit ((unsigned char) *cp) && *cp != '.') + error (1, 0, "%s is not a numeric branch", vbranch); + if (numdots (vbranch) != 2) + error (1, 0, "Only branches with two dots are supported: %s", vbranch); + vhead = xstrdup (vbranch); + cp = strrchr (vhead, '.'); + *cp = '\0'; + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + /* For rationale behind calling start_server before do_editor, see + commit.c */ + start_server (); + } +#endif + + if ( +#ifdef SERVER_SUPPORT + !server_active && +#endif + use_editor) + { + do_editor ((char *) NULL, &message, +#ifdef CLIENT_SUPPORT + current_parsed_root->isremote ? (char *) NULL : +#endif + repository, + (List *) NULL); + } + do_verify (&message, repository); + msglen = message == NULL ? 0 : strlen (message); + if (msglen == 0 || message[msglen - 1] != '\n') + { + char *nm = xmalloc (msglen + 2); + *nm = '\0'; + if (message != NULL) + { + (void) strcpy (nm, message); + free (message); + } + (void) strcat (nm + msglen, "\n"); + message = nm; + } + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + int err; + + if (vbranch[0] != '\0') + option_with_arg ("-b", vbranch); + option_with_arg ("-m", message ? message : ""); + if (keyword_opt != NULL) + option_with_arg ("-k", keyword_opt); + /* The only ignore processing which takes place on the server side + is the CVSROOT/cvsignore file. But if the user specified -I !, + the documented behavior is to not process said file. */ + if (ign_inhibit_server) + { + send_arg ("-I"); + send_arg ("!"); + } + wrap_send (); + + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + } + + logfp = stdin; + client_import_setup (repository); + err = import_descend (message, argv[1], argc - 2, argv + 2); + client_import_done (); + if (message) + free (message); + free (repository); + free (vbranch); + free (vhead); + send_to_server ("import\012", 0); + err += get_responses_and_close (); + return err; + } +#endif + + if (!safe_location ( NULL )) + { + error (1, 0, "attempt to import the repository"); + } + + /* + * Make all newly created directories writable. Should really use a more + * sophisticated security mechanism here. + */ + (void) umask (cvsumask); + make_directories (repository); + + /* Create the logfile that will be logged upon completion */ + if ((logfp = cvs_temp_file (&tmpfile)) == NULL) + error (1, errno, "cannot create temporary file `%s'", tmpfile); + /* On systems where we can unlink an open file, do so, so it will go + away no matter how we exit. FIXME-maybe: Should be checking for + errors but I'm not sure which error(s) we get if we are on a system + where one can't unlink open files. */ + (void) CVS_UNLINK (tmpfile); + (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]); + (void) fprintf (logfp, "Release Tags:\t"); + for (i = 2; i < argc; i++) + (void) fprintf (logfp, "%s\n\t\t", argv[i]); + (void) fprintf (logfp, "\n"); + + /* Just Do It. */ + err = import_descend (message, argv[1], argc - 2, argv + 2); + if (conflicts) + { + if (!really_quiet) + { + char buf[20]; + + cvs_output_tagged ("+importmergecmd", NULL); + cvs_output_tagged ("newline", NULL); + sprintf (buf, "%d", conflicts); + cvs_output_tagged ("conflicts", buf); + cvs_output_tagged ("text", " conflicts created by this import."); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("text", + "Use the following command to help the merge:"); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("text", "\t"); + cvs_output_tagged ("text", program_name); + if (CVSroot_cmdline != NULL) + { + cvs_output_tagged ("text", " -d "); + cvs_output_tagged ("text", CVSroot_cmdline); + } + cvs_output_tagged ("text", " checkout -j"); + cvs_output_tagged ("mergetag1", ""); + cvs_output_tagged ("text", " -j"); + cvs_output_tagged ("mergetag2", argv[2]); + cvs_output_tagged ("text", " "); + cvs_output_tagged ("repository", argv[0]); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("-importmergecmd", NULL); + } + + /* FIXME: I'm not sure whether we need to put this information + into the loginfo. If we do, then note that it does not + report any required -d option. There is no particularly + clean way to tell the server about the -d option used by + the client. */ + (void) fprintf (logfp, "\n%d conflicts created by this import.\n", + conflicts); + (void) fprintf (logfp, + "Use the following command to help the merge:\n\n"); + (void) fprintf (logfp, "\t%s checkout ", program_name); + (void) fprintf (logfp, "-j%s:yesterday -j%s %s\n\n", + argv[1], argv[1], argv[0]); + } + else + { + if (!really_quiet) + cvs_output ("\nNo conflicts created by this import\n\n", 0); + (void) fprintf (logfp, "\nNo conflicts created by this import\n\n"); + } + + /* + * Write out the logfile and clean up. + */ + ulist = getlist (); + p = getnode (); + p->type = UPDATE; + p->delproc = update_delproc; + p->key = xstrdup ("- Imported sources"); + 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 = li; + (void) addnode (ulist, p); + Update_Logfile (repository, message, logfp, ulist); + dellist (&ulist); + if (fclose (logfp) < 0) + error (0, errno, "error closing %s", tmpfile); + + /* Make sure the temporary file goes away, even on systems that don't let + you delete a file that's in use. */ + if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno)) + error (0, errno, "cannot remove %s", tmpfile); + free (tmpfile); + + if (message) + free (message); + free (repository); + free (vbranch); + free (vhead); + + return (err); +} + +/* Process all the files in ".", then descend into other directories. + Returns 0 for success, or >0 on error (in which case a message + will have been printed). */ +static int +import_descend (char *message, char *vtag, int targc, char **targv) +{ + DIR *dirp; + struct dirent *dp; + int err = 0; + List *dirlist = NULL; + + /* first, load up any per-directory ignore lists */ + ign_add_file (CVSDOTIGNORE, 1); + wrap_add_file (CVSDOTWRAPPER, 1); + + if ((dirp = CVS_OPENDIR (".")) == NULL) + { + error (0, errno, "cannot open directory"); + err++; + } + else + { + errno = 0; + while ((dp = CVS_READDIR (dirp)) != NULL) + { + if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0) + goto one_more_time_boys; +#ifdef SERVER_SUPPORT + /* CVS directories are created in the temp directory by + server.c because it doesn't special-case import. So + don't print a message about them, regardless of -I!. */ + if (server_active && strcmp (dp->d_name, CVSADM) == 0) + goto one_more_time_boys; +#endif + if (ign_name (dp->d_name)) + { + add_log ('I', dp->d_name); + goto one_more_time_boys; + } + + if ( +#ifdef DT_DIR + (dp->d_type == DT_DIR + || (dp->d_type == DT_UNKNOWN && isdir (dp->d_name))) +#else + isdir (dp->d_name) +#endif + && !wrap_name_has (dp->d_name, WRAP_TOCVS) + ) + { + Node *n; + + if (dirlist == NULL) + dirlist = getlist(); + + n = getnode(); + n->key = xstrdup (dp->d_name); + addnode(dirlist, n); + } + else if ( +#ifdef DT_DIR + dp->d_type == DT_LNK + || (dp->d_type == DT_UNKNOWN && islink (dp->d_name)) +#else + islink (dp->d_name) +#endif + ) + { + add_log ('L', dp->d_name); + err++; + } + else + { +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + err += client_process_import_file (message, dp->d_name, + vtag, targc, targv, + repository, + keyword_opt != NULL && + keyword_opt[0] == 'b', + use_file_modtime); + else +#endif + err += process_import_file (message, dp->d_name, + vtag, targc, targv); + } + one_more_time_boys: + errno = 0; + } + if (errno != 0) + { + error (0, errno, "cannot read directory"); + ++err; + } + (void) CVS_CLOSEDIR (dirp); + } + + if (dirlist != NULL) + { + Node *head, *p; + + head = dirlist->list; + for (p = head->next; p != head; p = p->next) + { + err += import_descend_dir (message, p->key, vtag, targc, targv); + } + + dellist(&dirlist); + } + + return (err); +} + +/* + * Process the argument import file. + */ +static int +process_import_file (char *message, char *vfile, char *vtag, int targc, char **targv) +{ + 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; + char *free_opt = NULL; + char *our_opt = keyword_opt; + + 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); + +#ifdef SERVER_SUPPORT + /* The most reliable information on whether the file is binary + is what the client told us. That is because if the client had + the wrong idea about binaryness, it corrupted the file, so + we might as well believe the client. */ + if (server_active) + { + Node *node; + List *entries; + + /* Reading all the entries for each file is fairly silly, and + probably slow. But I am too lazy at the moment to do + anything else. */ + entries = Entries_Open (0, NULL); + node = findnode_fn (entries, vfile); + if (node != NULL) + { + Entnode *entdata = node->data; + + if (entdata->type == ENT_FILE) + { + assert (entdata->options[0] == '-' + && entdata->options[1] == 'k'); + our_opt = xstrdup (entdata->options + 2); + free_opt = our_opt; + } + } + Entries_Close (entries); + } +#endif + + retval = add_rcs_file (message, rcs, vfile, vhead, our_opt, + vbranch, vtag, targc, targv, + NULL, 0, logfp); + if (free_opt != NULL) + free (free_opt); + free (rcs); + return retval; + } + free (attic_name); + inattic = 1; + } + + free (rcs); + /* + * an rcs file exists. have to do things the official, slow, way. + */ + return (update_rcs_file (message, vfile, vtag, targc, targv, inattic)); +} + +/* + * The RCS file exists; update it by adding the new import file to the + * (possibly already existing) vendor branch. + */ +static int +update_rcs_file (char *message, char *vfile, char *vtag, int targc, char **targv, int inattic) +{ + Vers_TS *vers; + int letter; + char *tocvsPath; + char *expand; + 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)) + { + int different; + + /* + * The rcs file does have a revision on the vendor branch. Compare + * this revision with the import file; if they match exactly, there + * is no need to install the new import file as a new revision to the + * branch. Just tag the revision with the new import tags. + * + * This is to try to cut down the number of "C" conflict messages for + * locally modified import source files. + */ + tocvsPath = wrap_tocvs_process_file (vfile); + /* FIXME: Why don't we pass tocvsPath to RCS_cmp_file if it is + not NULL? */ + expand = vers->srcfile->expand != NULL && + vers->srcfile->expand[0] == 'b' ? "-kb" : "-ko"; + different = RCS_cmp_file( vers->srcfile, vers->vn_rcs, (char **)NULL, + (char *)NULL, expand, vfile ); + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + + if (!different) + { + int retval = 0; + + /* + * The two files are identical. Just update the tags, print the + * "U", signifying that the file has changed, but needs no + * attention, and we're done. + */ + if (add_tags (vers->srcfile, vfile, vtag, targc, targv)) + retval = 1; + add_log ('U', vfile); + freevers_ts (&vers); + return (retval); + } + } + + /* We may have failed to parse the RCS file; check just in case */ + if (vers->srcfile == NULL || + add_rev (message, vers->srcfile, vfile, vers->vn_rcs) || + add_tags (vers->srcfile, vfile, vtag, targc, targv)) + { + freevers_ts (&vers); + return (1); + } + + if (vers->srcfile->branch == NULL || inattic || + strcmp (vers->srcfile->branch, vbranch) != 0) + { + conflicts++; + letter = 'C'; + } + else + letter = 'U'; + add_log (letter, vfile); + + freevers_ts (&vers); + return (0); +} + +/* + * Add the revision to the vendor branch + */ +static int +add_rev (char *message, RCSNode *rcs, char *vfile, char *vers) +{ + int locked, status, ierrno; + char *tocvsPath; + + if (noexec) + return (0); + + locked = 0; + if (vers != NULL) + { + /* Before RCS_lock existed, we were directing stdout, as well as + stderr, from the RCS command, to DEVNULL. I wouldn't guess that + was necessary, but I don't know for sure. */ + /* Earlier versions of this function printed a `fork failed' error + when RCS_lock returned an error code. That's not appropriate + now that RCS_lock is librarified, but should the error text be + preserved? */ + if (RCS_lock (rcs, vbranch, 1) != 0) + return 1; + locked = 1; + RCS_rewrite (rcs, NULL, NULL); + } + tocvsPath = wrap_tocvs_process_file (vfile); + + status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath, + message, vbranch, + (RCS_FLAGS_QUIET | RCS_FLAGS_KEEPFILE + | (use_file_modtime ? RCS_FLAGS_MODTIME : 0))); + ierrno = errno; + + if ((tocvsPath != NULL) && (unlink_file_dir (tocvsPath) < 0)) + error (0, errno, "cannot remove %s", tocvsPath); + + if (status) + { + if (!noexec) + { + fperrmsg (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) + { + (void) RCS_unlock(rcs, vbranch, 0); + RCS_rewrite (rcs, NULL, NULL); + } + return (1); + } + return (0); +} + +/* + * Add the vendor branch tag and all the specified import release tags to the + * RCS file. The vendor branch tag goes on the branch root (1.1.1) while the + * vendor release tags go on the newly added leaf of the branch (1.1.1.1, + * 1.1.1.2, ...). + */ +static int +add_tags (RCSNode *rcs, char *vfile, char *vtag, int targc, char **targv) +{ + int i, ierrno; + Vers_TS *vers; + int retcode = 0; + struct file_info finfo; + + if (noexec) + return (0); + + if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0) + { + ierrno = errno; + fperrmsg (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->path); + return (1); + } + RCS_rewrite (rcs, NULL, 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) + RCS_rewrite (rcs, NULL, NULL); + else + { + ierrno = errno; + fperrmsg (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->path); + } + } + freevers_ts (&vers); + return (0); +} + +/* + * Stolen from rcs/src/rcsfnms.c, and adapted/extended. + */ +struct compair +{ + char *suffix, *comlead; +}; + +static const struct compair comtable[] = +{ + +/* + * comtable pairs each filename suffix with a comment leader. The comment + * leader is placed before each line generated by the $Log keyword. This + * table is used to guess the proper comment leader from the working file's + * suffix during initial ci (see InitAdmin()). Comment leaders are needed for + * languages without multiline comments; for others they are optional. + * + * 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", "-- "}, + {"adb", "-- "}, + {"asm", ";; "}, /* assembler (MS-DOS) */ + {"ads", "-- "}, /* Ada */ + {"bas", "' "}, /* Visual Basic code */ + {"bat", ":: "}, /* batch (MS-DOS) */ + {"body", "-- "}, /* Ada */ + {"c", " * "}, /* C */ + {"c++", "// "}, /* C++ in all its infinite guises */ + {"cc", "// "}, + {"cpp", "// "}, + {"cxx", "// "}, + {"m", "// "}, /* Objective-C */ + {"cl", ";;; "}, /* Common Lisp */ + {"cmd", ":: "}, /* command (OS/2) */ + {"cmf", "c "}, /* CM Fortran */ + {"cs", " * "}, /* C* */ + {"csh", "# "}, /* shell */ + {"dlg", " * "}, /* MS Windows dialog file */ + {"e", "# "}, /* efl */ + {"epsf", "% "}, /* encapsulated postscript */ + {"epsi", "% "}, /* encapsulated postscript */ + {"el", "; "}, /* Emacs Lisp */ + {"f", "c "}, /* Fortran */ + {"for", "c "}, + {"frm", "' "}, /* Visual Basic form */ + {"h", " * "}, /* C-header */ + {"hh", "// "}, /* C++ header */ + {"hpp", "// "}, + {"hxx", "// "}, + {"in", "# "}, /* for Makefile.in */ + {"l", " * "}, /* lex (conflict between lex and + * franzlisp) */ + {"mac", ";; "}, /* macro (DEC-10, MS-DOS, PDP-11, + * VMS, etc) */ + {"mak", "# "}, /* makefile, e.g. Visual C++ */ + {"me", ".\\\" "}, /* me-macros t/nroff */ + {"ml", "; "}, /* mocklisp */ + {"mm", ".\\\" "}, /* mm-macros t/nroff */ + {"ms", ".\\\" "}, /* ms-macros t/nroff */ + {"man", ".\\\" "}, /* man-macros t/nroff */ + {"1", ".\\\" "}, /* feeble attempt at man pages... */ + {"2", ".\\\" "}, + {"3", ".\\\" "}, + {"4", ".\\\" "}, + {"5", ".\\\" "}, + {"6", ".\\\" "}, + {"7", ".\\\" "}, + {"8", ".\\\" "}, + {"9", ".\\\" "}, + {"p", " * "}, /* pascal */ + {"pas", " * "}, + {"pl", "# "}, /* perl (conflict with Prolog) */ + {"ps", "% "}, /* postscript */ + {"psw", "% "}, /* postscript wrap */ + {"pswm", "% "}, /* postscript wrap */ + {"r", "# "}, /* ratfor */ + {"rc", " * "}, /* Microsoft Windows resource file */ + {"red", "% "}, /* psl/rlisp */ +#ifdef sparc + {"s", "! "}, /* assembler */ +#endif +#ifdef mc68000 + {"s", "| "}, /* assembler */ +#endif +#ifdef pdp11 + {"s", "/ "}, /* assembler */ +#endif +#ifdef vax + {"s", "# "}, /* assembler */ +#endif +#ifdef __ksr__ + {"s", "# "}, /* assembler */ + {"S", "# "}, /* Macro assembler */ +#endif + {"sh", "# "}, /* shell */ + {"sl", "% "}, /* psl */ + {"spec", "-- "}, /* Ada */ + {"tex", "% "}, /* tex */ + {"y", " * "}, /* yacc */ + {"ye", " * "}, /* yacc-efl */ + {"yr", " * "}, /* yacc-ratfor */ + {"", "# "}, /* default for empty suffix */ + {NULL, "# "} /* default for unknown suffix; */ +/* must always be last */ +}; + + + +static char * +get_comment (const char *user) +{ + char *cp, *suffix; + char *suffix_path; + int i; + char *retval; + + suffix_path = xmalloc (strlen (user) + 5); + cp = strrchr (user, '.'); + if (cp != NULL) + { + cp++; + + /* + * Convert to lower-case, since we are not concerned about the + * case-ness of the suffix. + */ + (void) strcpy (suffix_path, cp); + for (cp = suffix_path; *cp; cp++) + if (isupper ((unsigned char) *cp)) + *cp = tolower (*cp); + suffix = suffix_path; + } + else + suffix = ""; /* will use the default */ + for (i = 0;; i++) + { + 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) + { + retval = comtable[i].comlead; + break; + } + } + free (suffix_path); + return retval; +} + +/* Create a new RCS file from scratch. + * + * This probably should be moved to rcs.c now that it is called from + * places outside import.c. + * + * INPUTS + * message Log message for the addition. Not used if add_vhead == NULL. + * rcs Filename of the RCS file to create. + * user Filename of the file to serve as the contents of the initial + * revision. Even if add_vhead is NULL, we use this to determine + * the modes to give the new RCS file. + * add_vhead Revision number of head that we are adding. Normally 1.1 but + * could be another revision as long as ADD_VBRANCH is a branch + * from it. If NULL, then just add an empty file without any + * revisions (similar to the one created by "rcs -i"). + * key_opt Keyword expansion mode, e.g., "b" for binary. NULL means the + * default behavior. + * add_vbranch + * Vendor branch to import to, or NULL if none. If non-NULL, then + * vtag should also be non-NULL. + * vtag + * targc Number of elements in TARGV. + * targv The list of tags to attached to this imported revision. + * desctext If non-NULL, description for the file. If NULL, the + * description will be empty. + * desclen The number of bytes in desctext. + * add_logfp Write errors to here as well as via error (), or NULL if we + * should use only error (). + * + * RETURNS + * Return value is 0 for success, or nonzero for failure (in which + * case an error message will have already been printed). + */ +int +add_rcs_file (const char *message, const char *rcs, const char *user, + const char *add_vhead, const char *key_opt, + const char *add_vbranch, const char *vtag, int targc, + char **targv, const char *desctext, size_t desclen, + FILE *add_logfp) +{ + FILE *fprcs, *fpuser; + struct stat sb; + struct tm *ftm; + time_t now; + char altdate1[MAXDATELEN]; + char *author; + int i, ierrno, err = 0; + mode_t mode; + char *tocvsPath; + const char *userfile; + char *free_opt = NULL; + mode_t file_type; + + if (noexec) + return (0); + + /* Note that as the code stands now, the -k option overrides any + settings in wrappers (whether CVSROOT/cvswrappers, -W, or + whatever). Some have suggested this should be the other way + around. As far as I know the documentation doesn't say one way + or the other. Before making a change of this sort, should think + about what is best, document it (in cvs.texinfo and NEWS), &c. */ + + if (key_opt == NULL) + { + if (wrap_name_has (user, WRAP_RCSOPTION)) + { + key_opt = free_opt = wrap_rcsoption (user, 0); + } + } + + tocvsPath = wrap_tocvs_process_file (user); + userfile = (tocvsPath == NULL ? user : tocvsPath); + + /* Opening in text mode is probably never the right thing for the + server (because the protocol encodes text files in a fashion + which does not depend on what the client or server OS is, as + documented in cvsclient.texi), but as long as the server just + runs on unix it is a moot point. */ + + /* If PreservePermissions is set, then make sure that the file + is a plain file before trying to open it. Longstanding (although + often unpopular) CVS behavior has been to follow symlinks, so we + maintain that behavior if PreservePermissions is not on. + + NOTE: this error message used to be `cannot fstat', but is now + `cannot lstat'. I don't see a way around this, since we must + stat the file before opening it. -twp */ + + if (CVS_LSTAT (userfile, &sb) < 0) + { + /* not fatal, continue import */ + if (add_logfp != NULL) + fperrmsg (add_logfp, 0, errno, + "ERROR: cannot lstat file %s", userfile); + error (0, errno, "cannot lstat file %s", userfile); + goto read_error; + } + file_type = sb.st_mode & S_IFMT; + + fpuser = NULL; + if (!preserve_perms || file_type == S_IFREG) + { + fpuser = CVS_FOPEN (userfile, + ((key_opt != NULL && strcmp (key_opt, "b") == 0) + ? "rb" + : "r") + ); + if (fpuser == NULL) + { + /* not fatal, continue import */ + if (add_logfp != NULL) + fperrmsg (add_logfp, 0, errno, + "ERROR: cannot read file %s", userfile); + error (0, errno, "ERROR: cannot read file %s", userfile); + goto read_error; + } + } + + fprcs = CVS_FOPEN (rcs, "w+b"); + if (fprcs == NULL) + { + ierrno = errno; + goto write_error_noclose; + } + + /* + * putadmin() + */ + if (add_vhead != NULL) + { + if (fprintf (fprcs, "head %s;\012", add_vhead) < 0) + goto write_error; + } + else + { + if (fprintf (fprcs, "head ;\012") < 0) + goto write_error; + } + + if (add_vbranch != NULL) + { + if (fprintf (fprcs, "branch %s;\012", add_vbranch) < 0) + goto write_error; + } + if (fprintf (fprcs, "access ;\012") < 0 || + fprintf (fprcs, "symbols ") < 0) + { + goto write_error; + } + + for (i = targc - 1; i >= 0; i--) + { + /* RCS writes the symbols backwards */ + assert (add_vbranch != NULL); + if (fprintf (fprcs, "%s:%s.1 ", targv[i], add_vbranch) < 0) + goto write_error; + } + + if (add_vbranch != NULL) + { + if (fprintf (fprcs, "%s:%s", vtag, add_vbranch) < 0) + goto write_error; + } + if (fprintf (fprcs, ";\012") < 0) + goto write_error; + + if (fprintf (fprcs, "locks ; strict;\012") < 0 || + /* XXX - make sure @@ processing works in the RCS file */ + fprintf (fprcs, "comment @%s@;\012", get_comment (user)) < 0) + { + goto write_error; + } + + if (key_opt != NULL && strcmp (key_opt, "kv") != 0) + { + if (fprintf (fprcs, "expand @%s@;\012", key_opt) < 0) + { + goto write_error; + } + } + + if (fprintf (fprcs, "\012") < 0) + goto write_error; + + /* Write the revision(s), with the date and author and so on + (that is "delta" rather than "deltatext" from rcsfile(5)). */ + if (add_vhead != NULL) + { + if (use_file_modtime) + now = sb.st_mtime; + else + (void) time (&now); + ftm = gmtime (&now); + (void) sprintf (altdate1, DATEFORM, + ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900), + ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour, + ftm->tm_min, ftm->tm_sec); + author = getcaller (); + + if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 || + fprintf (fprcs, "date %s; author %s; state Exp;\012", + altdate1, author) < 0) + goto write_error; + + if (fprintf (fprcs, "branches") < 0) + goto write_error; + if (add_vbranch != NULL) + { + if (fprintf (fprcs, " %s.1", add_vbranch) < 0) + goto write_error; + } + if (fprintf (fprcs, ";\012") < 0) + goto write_error; + + if (fprintf (fprcs, "next ;\012") < 0) + goto write_error; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Store initial permissions if necessary. */ + if (preserve_perms) + { + if (file_type == S_IFLNK) + { + char *link = xreadlink (userfile); + if (fprintf (fprcs, "symlink\t@") < 0 || + expand_at_signs (link, strlen (link), fprcs) < 0 || + fprintf (fprcs, "@;\012") < 0) + goto write_error; + free (link); + } + else + { + if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0) + goto write_error; + if (fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0) + goto write_error; + if (fprintf (fprcs, "permissions\t%o;\012", + sb.st_mode & 07777) < 0) + goto write_error; + switch (file_type) + { + case S_IFREG: break; + case S_IFCHR: + case S_IFBLK: +#ifdef HAVE_STRUCT_STAT_ST_RDEV + if (fprintf (fprcs, "special\t%s %lu;\012", + (file_type == S_IFCHR + ? "character" + : "block"), + (unsigned long) sb.st_rdev) < 0) + goto write_error; +#else + error (0, 0, +"can't import %s: unable to import device files on this system", +userfile); +#endif + break; + default: + error (0, 0, + "can't import %s: unknown kind of special file", + userfile); + } + } + } +#endif + + if (add_vbranch != NULL) + { + if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 || + fprintf (fprcs, "date %s; author %s; state Exp;\012", + altdate1, author) < 0 || + fprintf (fprcs, "branches ;\012") < 0 || + fprintf (fprcs, "next ;\012") < 0) + goto write_error; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Store initial permissions if necessary. */ + if (preserve_perms) + { + if (file_type == S_IFLNK) + { + char *link = xreadlink (userfile); + if (fprintf (fprcs, "symlink\t@") < 0 || + expand_at_signs (link, strlen (link), fprcs) < 0 || + fprintf (fprcs, "@;\012") < 0) + goto write_error; + free (link); + } + else + { + if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0 || + fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0 || + fprintf (fprcs, "permissions\t%o;\012", + sb.st_mode & 07777) < 0) + goto write_error; + + switch (file_type) + { + case S_IFREG: break; + case S_IFCHR: + case S_IFBLK: +#ifdef HAVE_STRUCT_STAT_ST_RDEV + if (fprintf (fprcs, "special\t%s %lu;\012", + (file_type == S_IFCHR + ? "character" + : "block"), + (unsigned long) sb.st_rdev) < 0) + goto write_error; +#else + error (0, 0, +"can't import %s: unable to import device files on this system", +userfile); +#endif + break; + default: + error (0, 0, + "cannot import %s: special file of unknown type", + userfile); + } + } + } +#endif + + if (fprintf (fprcs, "\012") < 0) + goto write_error; + } + } + + /* Now write the description (possibly empty). */ + if (fprintf (fprcs, "\012desc\012") < 0 || + fprintf (fprcs, "@") < 0) + goto write_error; + if (desctext != NULL) + { + /* The use of off_t not size_t for the second argument is very + strange, since we are dealing with something which definitely + fits in memory. */ + if (expand_at_signs (desctext, (off_t) desclen, fprcs) < 0) + goto write_error; + } + if (fprintf (fprcs, "@\012\012\012") < 0) + goto write_error; + + /* Now write the log messages and contents for the revision(s) (that + is, "deltatext" rather than "delta" from rcsfile(5)). */ + if (add_vhead != NULL) + { + if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 || + fprintf (fprcs, "log\012@") < 0) + goto write_error; + if (add_vbranch != NULL) + { + /* We are going to put the log message in the revision on the + branch. So putting it here too seems kind of redundant, I + guess (and that is what CVS has always done, anyway). */ + if (fprintf (fprcs, "Initial revision\012") < 0) + goto write_error; + } + else + { + if (expand_at_signs (message, (off_t) strlen (message), fprcs) < 0) + goto write_error; + } + if (fprintf (fprcs, "@\012") < 0 || + fprintf (fprcs, "text\012@") < 0) + { + goto write_error; + } + + /* Now copy over the contents of the file, expanding at signs. + If preserve_perms is set, do this only for regular files. */ + if (!preserve_perms || file_type == S_IFREG) + { + char buf[8192]; + unsigned int len; + + while (1) + { + len = fread (buf, 1, sizeof buf, fpuser); + if (len == 0) + { + if (ferror (fpuser)) + error (1, errno, "cannot read file %s for copying", + user); + break; + } + if (expand_at_signs (buf, len, fprcs) < 0) + goto write_error; + } + } + if (fprintf (fprcs, "@\012\012") < 0) + goto write_error; + if (add_vbranch != NULL) + { + if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 || + fprintf (fprcs, "log\012@") < 0 || + expand_at_signs (message, + (off_t) strlen (message), fprcs) < 0 || + fprintf (fprcs, "@\012text\012") < 0 || + fprintf (fprcs, "@@\012") < 0) + goto write_error; + } + } + + if (fclose (fprcs) == EOF) + { + ierrno = errno; + goto write_error_noclose; + } + /* Close fpuser only if we opened it to begin with. */ + if (fpuser != NULL) + { + if (fclose (fpuser) < 0) + error (0, errno, "cannot close %s", user); + } + + /* + * Fix the modes on the RCS files. The user modes of the original + * user file are propagated to the group and other modes as allowed + * by the repository umask, except that all write permissions are + * turned off. + */ + mode = (sb.st_mode | + (sb.st_mode & S_IRWXU) >> 3 | + (sb.st_mode & S_IRWXU) >> 6) & + ~cvsumask & + ~(S_IWRITE | S_IWGRP | S_IWOTH); + if (chmod (rcs, mode) < 0) + { + ierrno = errno; + if (add_logfp != NULL) + fperrmsg (add_logfp, 0, ierrno, + "WARNING: cannot change mode of file %s", rcs); + error (0, ierrno, "WARNING: cannot change mode of file %s", rcs); + err++; + } + 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: + ierrno = errno; + if (fclose (fprcs) < 0) + error (0, errno, "cannot close %s", rcs); +write_error_noclose: + if (fclose (fpuser) < 0) + error (0, errno, "cannot close %s", user); + if (add_logfp != NULL) + fperrmsg (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs); + error (0, ierrno, "ERROR: cannot write file %s", rcs); + if (ierrno == ENOSPC) + { + if (CVS_UNLINK (rcs) < 0) + error (0, errno, "cannot remove %s", rcs); + if (add_logfp != NULL) + fperrmsg (add_logfp, 0, 0, "ERROR: out of space - aborting"); + error (1, 0, "ERROR: out of space - aborting"); + } +read_error: + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + + if (free_opt != NULL) + free (free_opt); + + return (err + 1); +} + + + +/* + * Write SIZE bytes at BUF to FP, expanding @ signs into double @ + * signs. If an error occurs, return a negative value and set errno + * to indicate the error. If not, return a nonnegative value. + */ +int +expand_at_signs (const char *buf, off_t size, FILE *fp) +{ + register const char *cp, *next; + + cp = buf; + while ((next = memchr (cp, '@', size)) != NULL) + { + size_t len = ++next - cp; + if (fwrite (cp, 1, len, fp) != len) + return EOF; + if (putc ('@', fp) == EOF) + return EOF; + cp = next; + size -= len; + } + + if (fwrite (cp, 1, size, fp) != size) + return EOF; + + return 1; +} + +/* + * Write an update message to (potentially) the screen and the log file. + */ +static void +add_log (int ch, char *fname) +{ + if (!really_quiet) /* write to terminal */ + { + char buf[2]; + buf[0] = ch; + buf[1] = ' '; + cvs_output (buf, 2); + if (repos_len) + { + cvs_output (repository + repos_len + 1, 0); + cvs_output ("/", 1); + } + else if (repository[0] != '\0') + { + cvs_output (repository, 0); + cvs_output ("/", 1); + } + cvs_output (fname, 0); + cvs_output ("\n", 1); + } + + if (repos_len) /* write to logfile */ + (void) fprintf (logfp, "%c %s/%s\n", ch, + repository + repos_len + 1, fname); + else if (repository[0]) + (void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname); + else + (void) fprintf (logfp, "%c %s\n", ch, fname); +} + +/* + * This is the recursive function that walks the argument directory looking + * for sub-directories that have CVS administration files in them and updates + * them recursively. + * + * Note that we do not follow symbolic links here, which is a feature! + */ +static int +import_descend_dir (char *message, char *dir, char *vtag, int targc, char **targv) +{ + struct saved_cwd cwd; + char *cp; + int ierrno, err; + char *rcs = NULL; + + if (islink (dir)) + return (0); + if (save_cwd (&cwd)) + { + fperrmsg (logfp, 0, 0, "ERROR: cannot get working directory"); + return (1); + } + + /* Concatenate DIR to the end of REPOSITORY. */ + if (repository[0] == '\0') + { + char *new = xstrdup (dir); + free (repository); + repository = new; + } + else + { + 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 && !current_parsed_root->isremote) +#else + if (!quiet) +#endif + error (0, 0, "Importing %s", repository); + + if ( CVS_CHDIR (dir) < 0) + { + ierrno = errno; + fperrmsg (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository); + error (0, ierrno, "ERROR: cannot chdir to %s", repository); + err = 1; + goto out; + } +#ifdef CLIENT_SUPPORT + if (!current_parsed_root->isremote && !isdir (repository)) +#else + if (!isdir (repository)) +#endif + { + rcs = xmalloc (strlen (repository) + sizeof (RCSEXT) + 5); + (void) sprintf (rcs, "%s%s", repository, RCSEXT); + if (isfile (repository) || isfile(rcs)) + { + fperrmsg (logfp, 0, 0, + "ERROR: %s is a file, should be a directory!", + repository); + error (0, 0, "ERROR: %s is a file, should be a directory!", + repository); + err = 1; + goto out; + } + if (noexec == 0 && CVS_MKDIR (repository, 0777) < 0) + { + ierrno = errno; + fperrmsg (logfp, 0, ierrno, + "ERROR: cannot mkdir %s -- not added", repository); + error (0, ierrno, + "ERROR: cannot mkdir %s -- not added", repository); + err = 1; + goto out; + } + } + err = import_descend (message, vtag, targc, targv); + out: + if (rcs != NULL) + free (rcs); + if ((cp = strrchr (repository, '/')) != NULL) + *cp = '\0'; + else + repository[0] = '\0'; + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + return (err); +} diff --git a/contrib/cvs-1.12.9/src/lock.c b/contrib/cvs-1.12.9/src/lock.c new file mode 100644 index 0000000000..d23713bcd6 --- /dev/null +++ b/contrib/cvs-1.12.9/src/lock.c @@ -0,0 +1,1276 @@ +/* + * 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 source distribution. + * + * Set Lock + * + * 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 calls to add_rcs_file and 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 an option to disable locks for operations which only + read (see above for some of the consequences). + + 3. 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. */ + +#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 promotablelocks, it is just a pointer to the storage allocated + for the ->key field. */ + const char *repository; + + /* The name of the lock files. */ + char *file1; +#ifdef LOCK_COMPATIBILITY + char *file2; +#endif /* LOCK_COMPATIBILITY */ + + /* 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. */ + int free_repository; +}; + +static void remove_locks (void); +static int set_lock (struct lock *lock, int will_wait); +static void clear_lock (struct lock *lock); +static void set_lockers_name (struct stat *statp); + +/* 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 name of a promotablelock within a directory. + Or NULL if none. */ +static char *promotablelock; +/* 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; +static struct lock global_writelock; + +/* List of locks set by lock_tree_for_write. This is redundant + with locklist, sort of. */ +static List *lock_tree_list; + +/* LockDir from CVSROOT/config. */ +char *lock_dir; + + + +/* Return a newly malloc'd string containing the name of the lock for the + repository REPOSITORY and the lock file name within that directory + NAME. Also create the directories in which to put the lock file + if needed (if we need to, could save system call(s) by doing + that only if the actual operation fails. But for now we'll keep + things simple). */ +static char * +lock_name (const char *repository, const char *name) +{ + char *retval; + const char *p; + char *q; + const char *short_repos; + mode_t save_umask = 0000; + int saved_umask = 0; + + TRACE (TRACE_FLOW, "lock_name (%s, %s)", + repository ? repository : "(null)", name ? name : "(null)"); + + if (lock_dir == NULL) + { + /* This is the easy case. Because the lock files go directly + in the repository, no need to create directories or anything. */ + assert (name != NULL); + assert (repository != NULL); + retval = xmalloc (strlen (repository) + strlen (name) + 10); + (void) sprintf (retval, "%s/%s", repository, name); + } + else + { + struct stat sb; + mode_t new_mode = 0; + + /* The interesting part of the repository is the part relative + to CVSROOT. */ + assert (current_parsed_root != NULL); + assert (current_parsed_root->directory != NULL); + assert (strncmp (repository, current_parsed_root->directory, + strlen (current_parsed_root->directory)) == 0); + short_repos = repository + strlen (current_parsed_root->directory) + 1; + + if (strcmp (repository, current_parsed_root->directory) == 0) + short_repos = "."; + else + assert (short_repos[-1] == '/'); + + retval = xmalloc (strlen (lock_dir) + + strlen (short_repos) + + strlen (name) + + 10); + strcpy (retval, lock_dir); + q = retval + strlen (retval); + *q++ = '/'; + + strcpy (q, short_repos); + + /* In the common case, where the directory already exists, let's + keep it to one system call. */ + if (CVS_STAT (retval, &sb) < 0) + { + /* If we need to be creating more than one directory, we'll + get the existence_error here. */ + if (!existence_error (errno)) + error (1, errno, "cannot stat directory %s", retval); + } + else + { + if (S_ISDIR (sb.st_mode)) + goto created; + else + error (1, 0, "%s is not a directory", retval); + } + + /* Now add the directories one at a time, so we can create + them if needed. + + The idea behind the new_mode stuff is that the directory we + end up creating will inherit permissions from its parent + directory (we re-set new_mode with each EEXIST). CVSUMASK + isn't right, because typically the reason for LockDir is to + use a different set of permissions. We probably want to + inherit group ownership also (but we don't try to deal with + that, some systems do it for us either always or when g+s is on). + + We don't try to do anything about the permissions on the lock + files themselves. The permissions don't really matter so much + because the locks will generally be removed by the process + which created them. */ + + if (CVS_STAT (lock_dir, &sb) < 0) + error (1, errno, "cannot stat %s", lock_dir); + new_mode = sb.st_mode; + save_umask = umask (0000); + saved_umask = 1; + + p = short_repos; + while (1) + { + while (!ISSLASH (*p) && *p != '\0') + ++p; + if (ISSLASH (*p)) + { + strncpy (q, short_repos, p - short_repos); + q[p - short_repos] = '\0'; + if (!ISSLASH (q[p - short_repos - 1]) + && CVS_MKDIR (retval, new_mode) < 0) + { + int saved_errno = errno; + if (saved_errno != EEXIST) + error (1, errno, "cannot make directory %s", retval); + else + { + if (CVS_STAT (retval, &sb) < 0) + error (1, errno, "cannot stat %s", retval); + new_mode = sb.st_mode; + } + } + ++p; + } + else + { + strcpy (q, short_repos); + if (CVS_MKDIR (retval, new_mode) < 0 + && errno != EEXIST) + error (1, errno, "cannot make directory %s", retval); + goto created; + } + } + created:; + + strcat (retval, "/"); + strcat (retval, name); + + if (saved_umask) + { + assert (umask (save_umask) == 0000); + saved_umask = 0; + } + } + return retval; +} + + + +/* Remove the lock files. For interrupt purposes, it can be assumed that the + * first thing this function does is set lock->repository to NULL. + * + * INPUTS + * lock The lock to remove. + * free True if this lock directory will not5 be reused (free + * lock->repository if necessary). + */ +static void +remove_lock_files (struct lock *lock, int free_repository) +{ + TRACE (TRACE_FLOW, "remove_lock_files(%s)", lock->repository); + + /* If lock->file is set, the lock *might* have been created, but since + * Reader_Lock & lock_dir_for_write don'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 (lock->file1) + { + char *tmp = lock->file1; + lock->file1 = NULL; + if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) + error (0, errno, "failed to remove lock %s", tmp); + free (tmp); + } +#ifdef LOCK_COMPATIBILITY + if (lock->file2) + { + char *tmp = lock->file2; + lock->file2 = NULL; + if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) + error (0, errno, "failed to remove lock %s", tmp); + free (tmp); + } +#endif /* LOCK_COMPATIBILITY */ + + if (lock->have_lckdir) + { + char *tmp = lock_name (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); + } + + /* And free the repository string. We don't really have to set the + * repository string to NULL first since there is no harm in running any of + * the above code twice. + * + * Use SIG_beginCrSect since otherwise we might be interrupted between + * checking whether free_repository is set and freeing stuff. + */ + if (free_repository) + { + SIG_beginCrSect (); + if (lock->free_repository) + { + free ((char *)lock->repository); + lock->free_repository = 0; + } + lock->repository = NULL; + SIG_endCrSect (); + } +} + + + +/* + * Clean up outstanding read and write locks and free their storage. + */ +void +Simple_Lock_Cleanup (void) +{ + TRACE (TRACE_FUNCTION, "Simple_Lock_Cleanup()"); + + /* Avoid interrupts while accessing globals the interrupt handlers might + * make use of. + */ + SIG_beginCrSect(); + + /* clean up simple read locks (if any) */ + if (global_readlock.repository != NULL) + remove_lock_files (&global_readlock, 1); + /* See note in Lock_Cleanup() below. */ + SIG_endCrSect(); + + SIG_beginCrSect(); + + /* clean up simple write locks (if any) */ + if (global_writelock.repository != NULL) + remove_lock_files (&global_writelock, 1); + /* See note in Lock_Cleanup() below. */ + SIG_endCrSect(); +} + + + +/* + * Clean up all outstanding locks and free their storage. + * + * NOTES + * This function needs to be reentrant since a call to exit() can cause a + * call to this function, which can then be interrupted by a signal, which + * can cause a second call to this function. + * + * RETURNS + * Nothing. + */ +void +Lock_Cleanup (void) +{ + static short int never_run_again = 0; + + TRACE (TRACE_FUNCTION, "Lock_Cleanup()"); + + /* Since main_cleanup() always calls exit() (via error (1, ...)), we avoid + * allowing this function to be called twice as an optimization. + * + * If we are already in a signal critical section, assume we were called + * via the signal handler and set a flag which will prevent future calls. + * The only time that we should get into one of these functions otherwise + * while still in a critical section is if error(1,...) is called from a + * critical section, in which case we are exiting and interrupts are + * already being ignored. + * + * For Lock_Cleanup(), this is not a run_once variable since Lock_Cleanup() + * can be called to clean up the current lock set multiple times by the + * same run of a CVS command. + * + * For server_cleanup() and rcs_cleanup(), this is not a run_once variable + * since a call to the cleanup function from atexit() could be interrupted + * by the interrupt handler. + */ + if (never_run_again) return; + if (SIG_inCrSect()) never_run_again = 1; + + remove_locks (); + + /* Avoid being interrupted during calls which set globals to NULL. This + * avoids having interrupt handlers attempt to use these global variables + * in inconsistent states. + */ + SIG_beginCrSect(); + dellist (&lock_tree_list); + /* Unblocking allows any signal to be processed as soon as possible. This + * isn't really necessary, but since we know signals can cause us to be + * called, why not avoid having blocks of code run twice. + */ + SIG_endCrSect(); +} + + + +/* + * walklist proc for removing a list of locks + */ +static int +unlock_proc (Node *p, void *closure) +{ + remove_lock_files (p->data, 0); + return 0; +} + + + +/* + * Remove locks without discarding the lock information. + */ +static void +remove_locks (void) +{ + TRACE (TRACE_FLOW, "remove_locks()"); + + Simple_Lock_Cleanup (); + + /* clean up promotable locks (if any) */ + SIG_beginCrSect(); + if (locklist != NULL) + { + /* Use a tmp var since any of these functions could call exit, causing + * us to be called a second time. + */ + List *tmp = locklist; + locklist = NULL; + walklist (tmp, unlock_proc, NULL); + } + SIG_endCrSect(); +} + + + +/* + * Set the global readlock variable if it isn't already. + */ +static void +set_readlock_name (void) +{ + if (readlock == NULL) + { + readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40); + (void) sprintf (readlock, +#ifdef HAVE_LONG_FILE_NAMES + "%s.%s.%ld", CVSRFL, hostname, +#else + "%s.%ld", CVSRFL, +#endif + (long) getpid ()); + } +} + + + +/* + * Create a lock file for readers + */ +int +Reader_Lock (char *xrepository) +{ + int err = 0; + FILE *fp; + + TRACE (TRACE_FUNCTION, "Reader_Lock(%s)", xrepository); + + if (noexec || readonlyfs) + return 0; + + /* we only do one directory at a time for read locks! */ + if (global_readlock.repository != NULL) + { + error (0, 0, "Reader_Lock called while read locks set - Help!"); + return 1; + } + + set_readlock_name (); + + /* remember what we're locking (for Lock_Cleanup) */ + global_readlock.repository = xstrdup (xrepository); + global_readlock.free_repository = 1; + + /* get the lock dir for our own */ + if (set_lock (&global_readlock, 1) != L_OK) + { + error (0, 0, "failed to obtain dir lock in repository `%s'", + xrepository); + 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 */ + global_readlock.file1 = lock_name (xrepository, readlock); + if ((fp = CVS_FOPEN (global_readlock.file1, "w+")) == NULL + || fclose (fp) == EOF) + { + error (0, errno, "cannot create read lock in repository `%s'", + xrepository); + err = 1; + } + + /* free the lock dir */ + clear_lock (&global_readlock); + + return err; +} + + + +/* + * lock_exists() returns 0 if there is no lock file matching FILEPAT in + * the repository but not IGNORE; else 1 is returned, to indicate that the + * caller should sleep a while and try again. + * + * INPUTS + * repository The repository directory to search for locks. + * filepat The file name pattern to search for. + * ignore The name of a single file which can be ignored. + * + * GLOBALS + * lockdir The lock dir external to the repository, if any. + * + * RETURNS + * 0 No lock matching FILEPAT and not IGNORE exists. + * 1 Otherwise and on error. + * + * ERRORS + * In the case where errors are encountered reading the directory, a warning + * message is printed, 1 is is returned and ERRNO is left set. + */ +static int +lock_exists (const char *repository, const char *filepat, const char *ignore) +{ + char *lockdir; + char *line; + DIR *dirp; + struct dirent *dp; + struct stat sb; + int ret; +#ifdef CVS_FUDGELOCKS + time_t now; + (void)time (&now); +#endif + + TRACE (TRACE_FLOW, "lock_exists (%s, %s, %s)", + repository, filepat, ignore ? ignore : "(null)"); + + lockdir = lock_name (repository, ""); + lockdir[strlen (lockdir) - 1] = '\0'; /* remove trailing slash */ + + do { + if ((dirp = CVS_OPENDIR (lockdir)) == NULL) + error (1, 0, "cannot open directory %s", lockdir); + + ret = 0; + errno = 0; + while ((dp = CVS_READDIR (dirp)) != NULL) + { + if (CVS_FNMATCH (filepat, dp->d_name, 0) == 0) + { + /* FIXME: the basename conversion below should be replaced with + * a call to the GNULIB basename function once it is imported. + */ + /* ignore our plock, if any */ + if (ignore && !fncmp (ignore, dp->d_name)) + continue; + + line = xmalloc (strlen (lockdir) + 1 + strlen (dp->d_name) + 1); + (void)sprintf (line, "%s/%s", lockdir, dp->d_name); + if (CVS_STAT (line, &sb) != -1) + { +#ifdef CVS_FUDGELOCKS + /* + * If the create time of the file is more than CVSLCKAGE + * seconds ago, try to clean-up the lock file, and if + * successful, re-open the directory and try again. + */ + if (now >= (sb.st_ctime + CVSLCKAGE) && + CVS_UNLINK (line) != -1) + { + free (line); + ret = -1; + break; + } +#endif + set_lockers_name (&sb); + } + else + { + /* If the file doesn't exist, it just means that it + * disappeared between the time we did the readdir and the + * time we did the stat. + */ + if (!existence_error (errno)) + error (0, errno, "cannot stat %s", line); + } + errno = 0; + free (line); + ret = 1; + break; + } + errno = 0; + } + if (errno != 0) + error (0, errno, "error reading directory %s", repository); + + CVS_CLOSEDIR (dirp); + } while (ret < 0); + + if (lockdir != NULL) + free (lockdir); + return ret; +} + + + +/* + * readers_exist() returns 0 if there are no reader lock files remaining in + * the repository; else 1 is returned, to indicate that the caller should + * sleep a while and try again. + * + * See lock_exists() for argument detail. + */ +static int +readers_exist (const char *repository) +{ + TRACE (TRACE_FLOW, "readers_exist (%s)", repository); + + /* It is only safe to ignore a readlock set by our process if it was set as + * a safety measure to prevent older CVS processes from ignoring our + * promotable locks. The code to ignore these readlocks can be removed + * once it is deemed unlikely that anyone will be using CVS servers earlier + * than version 1.12.4. + */ + return lock_exists (repository, CVSRFLPAT, +#ifdef LOCK_COMPATIBILITY + findnode (locklist, repository) ? readlock : +#endif /* LOCK_COMPATIBILITY */ + NULL); +} + + + +/* + * promotable_exists() returns 0 if there is no promotable lock file in + * the repository; else 1 is returned, to indicate that the caller should + * sleep a while and try again. + * + * See lock_exists() for argument detail. + */ +static int +promotable_exists (const char *repository) +{ + TRACE (TRACE_FLOW, "promotable_exists (%s)", repository); + return lock_exists (repository, CVSPFLPAT, promotablelock); +} + + + +/* + * Lock a list of directories for writing + */ +static char *lock_error_repos; +static int lock_error; + + + +/* + * Create a lock file for potential writers returns L_OK if lock set ok, + * L_LOCKED if lock held by someone else or L_ERROR if an error occurred. + */ +static int +set_promotable_lock (struct lock *lock) +{ + int status; + FILE *fp; + + TRACE (TRACE_FUNCTION, "set_promotable_lock(%s)", + lock->repository ? lock->repository : "(null)"); + + if (promotablelock == NULL) + { + promotablelock = xmalloc (strlen (hostname) + sizeof (CVSPFL) + 40); + (void) sprintf (promotablelock, +#ifdef HAVE_LONG_FILE_NAMES + "%s.%s.%ld", CVSPFL, hostname, +#else + "%s.%ld", CVSPFL, +#endif + (long) getpid()); + } + + /* make sure the lock dir is ours (not necessarily unique to us!) */ + status = set_lock (lock, 0); + if (status == L_OK) + { + /* we now own a promotable lock - make sure there are no others */ + if (promotable_exists (lock->repository)) + { + /* clean up the lock dir */ + clear_lock (lock); + + /* indicate we failed due to read locks instead of error */ + return L_LOCKED; + } + + /* write the promotable-lock file */ + lock->file1 = lock_name (lock->repository, promotablelock); + if ((fp = CVS_FOPEN (lock->file1, "w+")) == NULL || fclose (fp) == EOF) + { + int xerrno = errno; + + if (CVS_UNLINK (lock->file1) < 0 && ! existence_error (errno)) + error (0, errno, "failed to remove lock %s", lock->file1); + + /* free the lock dir */ + clear_lock (lock); + + /* return the error */ + error (0, xerrno, + "cannot create promotable lock in repository `%s'", + lock->repository); + return L_ERROR; + } + +#ifdef LOCK_COMPATIBILITY + /* write the read-lock file. We only do this so that older versions of + * CVS will not think it is okay to create a write lock. When it is + * decided that versions of CVS earlier than 1.12.4 are not likely to + * be used, this code can be removed. + */ + set_readlock_name (); + lock->file2 = lock_name (lock->repository, readlock); + if ((fp = CVS_FOPEN (lock->file2, "w+")) == NULL || fclose (fp) == EOF) + { + int xerrno = errno; + + if ( CVS_UNLINK (lock->file2) < 0 && ! existence_error (errno)) + error (0, errno, "failed to remove lock %s", lock->file2); + + /* free the lock dir */ + clear_lock (lock); + + /* Remove the promotable lock. */ + lock->file2 = NULL; + remove_lock_files (lock, 0); + + /* return the error */ + error (0, xerrno, + "cannot create read lock in repository `%s'", + lock->repository); + return L_ERROR; + } +#endif /* LOCK_COMPATIBILITY */ + + clear_lock (lock); + + return L_OK; + } + else + return status; +} + + + +/* + * walklist proc for setting write locks. Mostly just a wrapper for the + * set_promotable_lock function, which has a prettier API, but no other good + * reason for existing separately. + * + * INPUTS + * p The current node, as determined by walklist(). + * closure Not used. + * + * GLOBAL INPUTS + * lock_error Any previous error encountered while attempting to get + * a lock. + * + * GLOBAL OUTPUTS + * lock_error Set if we encounter an error attempting to get axi + * promotable lock. + * lock_error_repos Set so that if we set lock_error later functions will + * be able to report where the other process's lock was + * encountered. + * + * RETURNS + * 0 for no error. + */ +static int +set_promotablelock_proc (Node *p, void *closure) +{ + /* if some lock was not OK, just skip this one */ + if (lock_error != L_OK) + return 0; + + /* apply the write lock */ + lock_error_repos = p->key; + lock_error = set_promotable_lock ((struct lock *)p->data); + return 0; +} + + + +/* + * Print out a message that the lock is still held, then sleep a while. + */ +static void +lock_wait (const char *repos) +{ + time_t now; + char *msg; + struct tm *tm_p; + + (void) time (&now); + tm_p = gmtime (&now); + msg = xmalloc (100 + strlen (lockers_name) + strlen (repos)); + sprintf (msg, "[%8.8s] waiting for %s's lock in %s", + (tm_p ? asctime (tm_p) : ctime (&now)) + 11, + lockers_name, repos); + error (0, 0, "%s", msg); + /* Call cvs_flusherr to ensure that the user sees this message as + soon as possible. */ + cvs_flusherr (); + free (msg); + (void)sleep (CVSLCKSLEEP); +} + + + +/* + * Print out a message when we obtain a lock. + */ +static void +lock_obtained (const char *repos) +{ + time_t now; + char *msg; + struct tm *tm_p; + + (void) time (&now); + tm_p = gmtime (&now); + msg = xmalloc (100 + strlen (repos)); + sprintf (msg, "[%8.8s] obtained lock in %s", + (tm_p ? asctime (tm_p) : ctime (&now)) + 11, repos); + error (0, 0, "%s", msg); + /* Call cvs_flusherr to ensure that the user sees this message as + soon as possible. */ + cvs_flusherr (); + free (msg); +} + + + +static int +lock_list_promotably (List *list) +{ + char *wait_repos; + + TRACE (TRACE_FLOW, "lock_list_promotably ()"); + + if (noexec) + return 0; + + if (readonlyfs) { + error (0, 0, + "promotable lock failed.\n\ +WARNING: Read-only repository access mode selected via `cvs -R'.\n\ +Attempting to write to a read-only filesystem is not allowed."); + return 1; + } + + /* We only know how to do one list at a time */ + if (locklist != (List *) NULL) + { + error (0, 0, + "lock_list_promotably called while promotable locks set - Help!"); + return 1; + } + + wait_repos = NULL; + for (;;) + { + /* try to lock everything on the list */ + lock_error = L_OK; /* init for set_promotablelock_proc */ + lock_error_repos = (char *) NULL; /* init for set_promotablelock_proc */ + locklist = list; /* init for Lock_Cleanup */ + if (lockers_name != NULL) + free (lockers_name); + lockers_name = xstrdup ("unknown"); + + (void) walklist (list, set_promotablelock_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 */ + 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 lock_list_promotably", + lock_error); + return 1; + } + } +} + + + +/* + * Set the static variable lockers_name appropriately, based on the stat + * structure passed in. + */ +static void +set_lockers_name (struct stat *statp) +{ + struct passwd *pw; + + if (lockers_name != NULL) + free (lockers_name); + if ((pw = (struct passwd *)getpwuid (statp->st_uid)) != + (struct passwd *)NULL) + { + lockers_name = xstrdup (pw->pw_name); + } + else + { + lockers_name = xmalloc (20); + (void)sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid); + } +} + + + +/* + * Persistently tries to make the directory "lckdir", which serves as a + * lock. + * + * #ifdef CVS_FUDGELOCKS + * If the create time on the directory is greater than CVSLCKAGE + * seconds old, just try to remove the directory. + * #endif + * + */ +static int +set_lock (struct lock *lock, int will_wait) +{ + int waited; + long us; + struct stat sb; + mode_t omask; +#ifdef CVS_FUDGELOCKS + time_t now; +#endif + + TRACE (TRACE_FLOW, "set_lock (%s, %d)", + lock->repository ? lock->repository : "(null)", will_wait); + + if (masterlock != NULL) + free (masterlock); + masterlock = lock_name (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. + */ + waited = 0; + us = 1; + lock->have_lckdir = 0; + for (;;) + { + int status = -1; + omask = umask (cvsumask); + SIG_beginCrSect (); + if (CVS_MKDIR (masterlock, 0777) == 0) + { + lock->have_lckdir = 1; + SIG_endCrSect (); + status = L_OK; + if (waited) + lock_obtained (lock->repository); + goto out; + } + SIG_endCrSect (); + out: + (void) umask (omask); + if (status != -1) + return status; + + if (errno != EEXIST) + { + error (0, errno, + "failed to create lock directory for `%s' (%s)", + lock->repository, masterlock); + return (L_ERROR); + } + + /* 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; + + error (0, errno, "couldn't stat lock directory `%s'", masterlock); + return (L_ERROR); + } + +#ifdef CVS_FUDGELOCKS + /* + * If the create time of the directory is more than CVSLCKAGE seconds + * ago, try to clean-up the lock directory, and if successful, just + * quietly retry to make it. + */ + (void) time (&now); + if (now >= (sb.st_ctime + CVSLCKAGE)) + { + if (CVS_RMDIR (masterlock) >= 0) + continue; + } +#endif + + /* set the lockers name */ + set_lockers_name (&sb); + + /* if he wasn't willing to wait, return an error */ + if (!will_wait) + return (L_LOCKED); + + /* if possible, try a very short sleep without a message */ + if (!waited && us < 1000) + { + us += us; + { + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = us * 1000; + (void)nanosleep (&ts, NULL); + continue; + } + } + + lock_wait (lock->repository); + waited = 1; + } +} + + + +/* + * Clear master lock. We don't have to recompute the lock name since + * clear_lock is never called except after a successful set_lock(). + */ +static void +clear_lock (struct lock *lock) +{ + SIG_beginCrSect (); + if (CVS_RMDIR (masterlock) < 0) + error (0, errno, "failed to remove lock dir `%s'", masterlock); + lock->have_lckdir = 0; + SIG_endCrSect (); +} + + + +/* + * Create a list of repositories to lock + */ +/* ARGSUSED */ +static int +lock_filesdoneproc (void *callerdat, int err, const char *repository, + const 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)->file1 = NULL; +#ifdef LOCK_COMPATIBILITY + ((struct lock *)p->data)->file2 = NULL; +#endif /* LOCK_COMPATIBILITY */ + ((struct lock *)p->data)->have_lckdir = 0; + ((struct lock *)p->data)->free_repository = 0; + + /* FIXME-KRP: this error condition should not simply be passed by. */ + if (p->key == NULL || addnode (lock_tree_list, p) != 0) + freenode (p); + return err; +} + + + +void +lock_tree_promotably (int argc, char **argv, int local, int which, int aflag) +{ + TRACE (TRACE_FUNCTION, "lock_tree_promotably (%d, argv, %d, %d, %d)", + argc, local, which, aflag); + + /* + * Run the recursion processor to find all the dirs to lock and lock all + * the dirs + */ + lock_tree_list = getlist (); + start_recursion + (NULL, lock_filesdoneproc, + NULL, NULL, NULL, argc, + argv, local, which, aflag, CVS_LOCK_NONE, + NULL, 0, NULL ); + sortlist (lock_tree_list, fsortcmp); + if (lock_list_promotably (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. + * + * We do not clear the dir lock after writing the lock file name since write + * locks are exclusive to all other locks. + */ +void +lock_dir_for_write (const char *repository) +{ + int waiting = 0; + + TRACE (TRACE_FLOW, "lock_dir_for_write (%s)", repository); + + if (repository != NULL + && (global_writelock.repository == NULL + || !strcmp (global_writelock.repository, repository))) + { + if (writelock == NULL) + { + writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40); + (void) sprintf (writelock, +#ifdef HAVE_LONG_FILE_NAMES + "%s.%s.%ld", CVSWFL, hostname, +#else + "%s.%ld", CVSWFL, +#endif + (long) getpid()); + } + + if (global_writelock.repository != NULL) + remove_lock_files (&global_writelock, 1); + + global_writelock.repository = xstrdup (repository); + global_writelock.free_repository = 1; + + for (;;) + { + FILE *fp; + + if (set_lock (&global_writelock, 1) != L_OK) + error (1, 0, "failed to obtain write lock in repository `%s'", + repository); + + /* check if readers exist */ + if (readers_exist (repository) + || promotable_exists (repository)) + { + clear_lock (&global_writelock); + lock_wait (repository); /* sleep a while and try again */ + waiting = 1; + continue; + } + + if (waiting) + lock_obtained (repository); + + /* write the write-lock file */ + global_writelock.file1 = lock_name (global_writelock.repository, + writelock); + if ((fp = CVS_FOPEN (global_writelock.file1, "w+")) == NULL + || fclose (fp) == EOF) + { + int xerrno = errno; + + if (CVS_UNLINK (global_writelock.file1) < 0 + && !existence_error (errno)) + { + error (0, errno, "failed to remove write lock %s", + global_writelock.file1); + } + + /* free the lock dir */ + clear_lock (&global_writelock); + + /* return the error */ + error (1, xerrno, + "cannot create write lock in repository `%s'", + global_writelock.repository); + } + + /* If we upgraded from a promotable lock, remove it. */ + if (locklist) + { + Node *p = findnode (locklist, repository); + if (p) + { + remove_lock_files ((struct lock *)p->data, 1); + delnode (p); + } + } + + break; + } + } +} diff --git a/contrib/cvs-1.12.9/src/log-buffer.c b/contrib/cvs-1.12.9/src/log-buffer.c new file mode 100644 index 0000000000..3ede2c0f84 --- /dev/null +++ b/contrib/cvs-1.12.9/src/log-buffer.c @@ -0,0 +1,207 @@ +/* CVS client logging buffer. + + 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 + +#include + +#include "cvs.h" +#include "buffer.h" + +#ifdef CLIENT_SUPPORT + +/* 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. + + This structure is the closure field of a log buffer. */ + +struct log_buffer +{ + /* The underlying buffer. */ + struct buffer *buf; + /* The file to log information to. */ + FILE *log; +}; + +static int log_buffer_input (void *, char *, int, int, int *); +static int log_buffer_output (void *, const char *, int, int *); +static int log_buffer_flush (void *); +static int log_buffer_block (void *, int); +static int log_buffer_shutdown (struct buffer *); + +/* Create a log buffer. */ + +static struct buffer * +log_buffer_initialize (struct buffer *buf, FILE *fp, int input, void (*memory) (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); +} + +/* The input function for a log buffer. */ + +static int +log_buffer_input (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; + + if (lb->buf->input == NULL) + abort (); + + status = (*lb->buf->input) (lb->buf->closure, data, need, size, got); + if (status != 0) + return status; + + if (*got > 0) + { + n_to_write = *got; + if (fwrite (data, 1, n_to_write, lb->log) != n_to_write) + error (0, errno, "writing to log file"); + } + + return 0; +} + +/* The output function for a log buffer. */ + +static int +log_buffer_output (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) + { + n_to_write = *wrote; + if (fwrite (data, 1, n_to_write, lb->log) != n_to_write) + error (0, errno, "writing to log file"); + } + + return 0; +} + +/* The flush function for a log buffer. */ + +static int +log_buffer_flush (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 (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 (struct buffer *buf) +{ + struct log_buffer *lb = (struct log_buffer *) buf->closure; + int retval; + + retval = buf_shutdown (lb->buf); + if (fclose (lb->log) < 0) + error (0, errno, "closing log file"); + return retval; +} + + +void +setup_logfiles (struct buffer **to_server_p, struct buffer **from_server_p) +{ + char *log = getenv ("CVS_CLIENT_LOG"); + + /* Set up logfiles, if any. + * + * We do this _after_ authentication on purpose. Wouldn't really like to + * worry about logging passwords... + */ + 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). */ + /* Note that if we create several connections in a single CVS client + (currently used by update.c), then the last set of logfiles will + overwrite the others. There is currently no way around this. */ + strcpy (p, ".in"); + fp = open_file (buf, "wb"); + if (fp == NULL) + error (0, errno, "opening to-server logfile %s", buf); + else + *to_server_p = log_buffer_initialize (*to_server_p, fp, 0, + (BUFMEMERRPROC) NULL); + + strcpy (p, ".out"); + fp = open_file (buf, "wb"); + if (fp == NULL) + error (0, errno, "opening from-server logfile %s", buf); + else + *from_server_p = log_buffer_initialize (*from_server_p, fp, 1, + (BUFMEMERRPROC) NULL); + + free (buf); + } +} + +#endif /* CLIENT_SUPPORT */ diff --git a/contrib/cvs-1.12.9/src/log-buffer.h b/contrib/cvs-1.12.9/src/log-buffer.h new file mode 100644 index 0000000000..ce812c5743 --- /dev/null +++ b/contrib/cvs-1.12.9/src/log-buffer.h @@ -0,0 +1,19 @@ +/* CVS client logging buffer. + + 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. */ + + +#ifndef LOG_BUFFER_H__ +#define LOG_BUFFER_H__ + +void setup_logfiles (struct buffer** to_server_p, struct buffer** from_server_p); + +#endif /* LOG_BUFFER_H__ */ diff --git a/contrib/cvs-1.12.9/src/log.c b/contrib/cvs-1.12.9/src/log.c new file mode 100644 index 0000000000..27a8ae79e7 --- /dev/null +++ b/contrib/cvs-1.12.9/src/log.c @@ -0,0 +1,1737 @@ +/* + * 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 source distribution. + * + * Print Log Information + * + * Prints the RCS "log" (rlog) information for the specified files. With no + * argument, prints the log information for all the files in the directory + * (recursive by default). + */ + +#include "cvs.h" + +/* 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; + /* Nonzero if first and last are inclusive. */ + int inclusive; +}; + +/* 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; + /* Whether first & last are to be included or excluded. */ + int inclusive; +}; + +/* 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; + /* Nonzero if the -S option was seen, meaning that the header/name + should be suppressed if no revisions are selected. */ + int sup_header; + /* 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 int rlog_proc (int argc, char **argv, char *xwhere, + char *mwhere, char *mfile, int shorten, + int local_specified, char *mname, char *msg); +static Dtype log_dirproc (void *callerdat, const char *dir, + const char *repository, const char *update_dir, + List *entries); +static int log_fileproc (void *callerdat, struct file_info *finfo); +static struct option_revlist *log_parse_revlist (const char *); +static void log_parse_date (struct log_data *, const char *); +static void log_parse_list (List **, const char *); +static struct revlist *log_expand_revlist (RCSNode *, struct option_revlist *, + int); +static void log_free_revlist (struct revlist *); +static int log_version_requested (struct log_data *, struct revlist *, + RCSNode *, RCSVers *); +static int log_symbol (Node *, void *); +static int log_count (Node *, void *); +static int log_fix_singledate (Node *, void *); +static int log_count_print (Node *, void *); +static void log_tree (struct log_data *, struct revlist *, + RCSNode *, const char *); +static void log_abranch (struct log_data *, struct revlist *, + RCSNode *, const char *); +static void log_version (struct log_data *, struct revlist *, + RCSNode *, RCSVers *, int); +static int log_branch (Node *, void *); +static int version_compare (const char *, const char *, int); + +static struct log_data log_data; +static int is_rlog; + +static const char *const log_usage[] = +{ + "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-S\tDo not print name/header if no revisions selected.\n", + "\t-b\tOnly list revisions on the default branch.\n", + "\t-r[revisions]\tA comma-separated list of revisions to print:\n", + "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n", + "\t rev1::rev2 Between rev1 and rev2, excluding rev1.\n", + "\t rev: rev and following revisions on the same branch.\n", + "\t rev:: After rev on the same branch.\n", + "\t :rev rev and previous revisions on the same branch.\n", + "\t ::rev rev and previous revisions on the same branch.\n", + "\t rev Just rev.\n", + "\t branch All revisions on the branch.\n", + "\t branch. The last revision on the branch.\n", + "\t-d dates\tA semicolon-separated list of dates\n", + "\t \t(D1key, "@@MYSELF") == 0) + /* It is a bare -w option. Note that we must send it as + -w rather than messing with getcaller() or something (which on + the client will return garbage). */ + ; + else + send_to_server (node->key, 0); + send_to_server ("\012", 0); + return 0; +} + + + +/* For each element in ARG, send an argument consisting of OPTION + concatenated with that element. */ +static void +send_arg_list (char *option, List *arg) +{ + if (arg == NULL) + return; + walklist (arg, send_one, option); +} + +#endif + + + +int +cvslog (int argc, char **argv) +{ + int c; + int err = 0; + int local = 0; + struct option_revlist **prl; + + is_rlog = (strcmp (cvs_cmd_name, "rlog") == 0); + + if (argc == -1) + usage (log_usage); + + memset (&log_data, 0, sizeof log_data); + prl = &log_data.revlist; + + optind = 0; + while ((c = getopt (argc, argv, "+bd:hlNSRr::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 'S': + log_data.sup_header = 1; + break; + case 'R': + log_data.nameonly = 1; + break; + case 'r': + *prl = log_parse_revlist (optarg); + prl = &(*prl)->next; + 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, "@@MYSELF"); + break; + case '?': + default: + usage (log_usage); + break; + } + } + argc -= optind; + argv += optind; + + wrap_setup (); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + struct datelist *p; + struct option_revlist *rp; + char datetmp[MAXDATELEN]; + + /* We're the local client. Fire up the remote server. */ + start_server (); + + if (is_rlog && !supported_request ("rlog")) + error (1, 0, "server does not support rlog"); + + ign_setup (); + + if (log_data.default_branch) + send_arg ("-b"); + + while (log_data.datelist != NULL) + { + p = log_data.datelist; + log_data.datelist = p->next; + send_to_server ("Argument -d\012", 0); + send_to_server ("Argument ", 0); + date_to_internet (datetmp, p->start); + send_to_server (datetmp, 0); + if (p->inclusive) + send_to_server ("<=", 0); + else + send_to_server ("<", 0); + date_to_internet (datetmp, p->end); + send_to_server (datetmp, 0); + send_to_server ("\012", 0); + if (p->start) + free (p->start); + if (p->end) + free (p->end); + free (p); + } + while (log_data.singledatelist != NULL) + { + p = log_data.singledatelist; + log_data.singledatelist = p->next; + send_to_server ("Argument -d\012", 0); + send_to_server ("Argument ", 0); + date_to_internet (datetmp, p->end); + send_to_server (datetmp, 0); + send_to_server ("\012", 0); + if (p->end) + free (p->end); + free (p); + } + + if (log_data.header) + send_arg ("-h"); + if (local) + send_arg("-l"); + if (log_data.notags) + send_arg("-N"); + if (log_data.sup_header) + send_arg("-S"); + if (log_data.nameonly) + send_arg("-R"); + if (log_data.long_header) + send_arg("-t"); + + while (log_data.revlist != NULL) + { + rp = log_data.revlist; + log_data.revlist = rp->next; + send_to_server ("Argument -r", 0); + if (rp->branchhead) + { + if (rp->first != NULL) + send_to_server (rp->first, 0); + send_to_server (".", 1); + } + else + { + if (rp->first != NULL) + send_to_server (rp->first, 0); + send_to_server (":", 1); + if (!rp->inclusive) + send_to_server (":", 1); + if (rp->last != NULL) + send_to_server (rp->last, 0); + } + send_to_server ("\012", 0); + if (rp->first) + free (rp->first); + if (rp->last) + free (rp->last); + free (rp); + } + send_arg_list ("-s", log_data.statelist); + dellist (&log_data.statelist); + send_arg_list ("-w", log_data.authorlist); + dellist (&log_data.authorlist); + send_arg ("--"); + + if (is_rlog) + { + int i; + for (i = 0; i < argc; i++) + send_arg (argv[i]); + send_to_server ("rlog\012", 0); + } + else + { + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server ("log\012", 0); + } + err = get_responses_and_close (); + return err; + } +#endif + + /* OK, now that we know we are local/server, we can resolve @@MYSELF + into our user name. */ + if (findnode (log_data.authorlist, "@@MYSELF") != NULL) + log_parse_list (&log_data.authorlist, getcaller ()); + + if (is_rlog) + { + DBM *db; + int i; + db = open_module (); + for (i = 0; i < argc; i++) + { + err += do_module (db, argv[i], MISC, "Logging", rlog_proc, + NULL, 0, local, 0, 0, NULL); + } + close_module (db); + } + else + { + err = rlog_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL, + NULL); + } + + while (log_data.revlist) + { + struct option_revlist *rl = log_data.revlist->next; + if (log_data.revlist->first) + free (log_data.revlist->first); + if (log_data.revlist->last) + free (log_data.revlist->last); + free (log_data.revlist); + log_data.revlist = rl; + } + while (log_data.datelist) + { + struct datelist *nd = log_data.datelist->next; + if (log_data.datelist->start) + free (log_data.datelist->start); + if (log_data.datelist->end) + free (log_data.datelist->end); + free (log_data.datelist); + log_data.datelist = nd; + } + while (log_data.singledatelist) + { + struct datelist *nd = log_data.singledatelist->next; + if (log_data.singledatelist->start) + free (log_data.singledatelist->start); + if (log_data.singledatelist->end) + free (log_data.singledatelist->end); + free (log_data.singledatelist); + log_data.singledatelist = nd; + } + dellist (&log_data.statelist); + dellist (&log_data.authorlist); + + return err; +} + + + +static int +rlog_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile, + int shorten, int local, char *mname, char *msg) +{ + /* Begin section which is identical to patch_proc--should this + be abstracted out somehow? */ + char *myargv[2]; + int err = 0; + int which; + char *repository = NULL; + char *where; + + if (is_rlog) + { + repository = xmalloc (strlen (current_parsed_root->directory) + + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); + (void)sprintf (repository, "%s/%s", + current_parsed_root->directory, argv[0]); + where = xmalloc (strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + + 1); + (void)strcpy (where, argv[0]); + + /* If mfile isn't null, we need to set up to do only part of theu + * module. + */ + if (mfile != NULL) + { + char *cp; + char *path; + + /* If the portion of the module is a path, put the dir part on + * repos. + */ + if ((cp = strrchr (mfile, '/')) != NULL) + { + *cp = '\0'; + (void)strcat (repository, "/"); + (void)strcat (repository, mfile); + (void)strcat (where, "/"); + (void)strcat (where, mfile); + mfile = cp + 1; + } + + /* take care of the rest */ + path = xmalloc (strlen (repository) + strlen (mfile) + 5); + (void)sprintf (path, "%s/%s", repository, mfile); + if (isdir (path)) + { + /* directory means repository gets the dir tacked on */ + (void)strcpy (repository, path); + (void)strcat (where, "/"); + (void)strcat (where, mfile); + } + else + { + myargv[0] = argv[0]; + myargv[1] = mfile; + argc = 2; + argv = myargv; + } + free (path); + } + + /* cd to the starting repository */ + if (CVS_CHDIR (repository) < 0) + { + error (0, errno, "cannot chdir to %s", repository); + free (repository); + free (where); + return 1; + } + /* End section which is identical to patch_proc. */ + + which = W_REPOS | W_ATTIC; + } + else + { + repository = NULL; + where = NULL; + which = W_LOCAL | W_REPOS | W_ATTIC; + } + + err = start_recursion (log_fileproc, NULL, log_dirproc, + NULL, &log_data, + argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ, + where, 1, repository); + + if (!(which & W_LOCAL)) free (repository); + if (where) free (where); + + return err; +} + + + +/* + * Parse a revision list specification. + */ +static struct option_revlist * +log_parse_revlist (const char *argstring) +{ + char *orig_copy, *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) + argstring = ""; + + 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. */ + orig_copy = copy = xstrdup (argstring); + while (copy != NULL) + { + char *comma; + struct option_revlist *r; + + comma = strchr (copy, ','); + if (comma != NULL) + *comma++ = '\0'; + + r = xmalloc (sizeof *r); + r->next = NULL; + r->first = copy; + r->branchhead = 0; + r->last = strchr (copy, ':'); + if (r->last != NULL) + { + *r->last++ = '\0'; + r->inclusive = (*r->last != ':'); + if (!r->inclusive) + r->last++; + } + else + { + r->last = r->first; + r->inclusive = 1; + if (r->first[0] != '\0' && r->first[strlen (r->first) - 1] == '.') + { + r->branchhead = 1; + r->first[strlen (r->first) - 1] = '\0'; + } + } + + if (*r->first == '\0') + r->first = NULL; + if (*r->last == '\0') + r->last = NULL; + + if (r->first != NULL) + r->first = xstrdup (r->first); + if (r->last != NULL) + r->last = xstrdup (r->last); + + *pr = r; + pr = &r->next; + + copy = comma; + } + + free (orig_copy); + return ret; +} + + + +/* + * Parse a date specification. + */ +static void +log_parse_date (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. */ + orig_copy = copy = xstrdup (argstring); + while (copy != NULL) + { + struct datelist *nd, **pd; + char *cpend, *cp, *ds, *de; + + nd = 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 (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] = '\0'; + } + + if (*plist == NULL) + *plist = getlist (); + if (addnode (*plist, p) != 0) + freenode (p); + + if (cp == NULL) + break; + + argstring = cp + 1; + } +} + + + +static int +printlock_proc (Node *lock, void *foo) +{ + cvs_output ("\n\t", 2); + cvs_output (lock->data, 0); + cvs_output (": ", 2); + cvs_output (lock->key, 0); + return 0; +} + + + +/* + * Do an rlog on a file + */ +static int +log_fileproc (void *callerdat, struct file_info *finfo) +{ + struct log_data *log_data = callerdat; + Node *p; + int selrev = -1; + RCSNode *rcsfile; + char buf[50]; + struct revlist *revlist = NULL; + struct log_data_and_rcs log_data_and_rcs; + + if ((rcsfile = finfo->rcs) == NULL) + { + /* no rcs file. What *do* we know about this file? */ + p = findnode (finfo->entries, finfo->file); + if (p != NULL) + { + Entnode *e = p->data; + + if (e->version[0] == '0' && e->version[1] == '\0') + { + if (!really_quiet) + error (0, 0, "%s has been added, but not committed", + finfo->file); + return 0; + } + } + + if (!really_quiet) + error (0, 0, "nothing known about %s", finfo->file); + + return 1; + } + + if (log_data->sup_header || !log_data->nameonly) + { + + /* 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); + if (log_data->sup_header + || (!log_data->header && !log_data->long_header)) + { + 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, + &log_data_and_rcs); + + selrev = walklist (rcsfile->versions, log_count_print, + &log_data_and_rcs); + if (log_data->sup_header && selrev == 0) + { + log_free_revlist (revlist); + return 0; + } + } + + } + + if (log_data->nameonly) + { + cvs_output (rcsfile->path, 0); + cvs_output ("\n", 1); + log_free_revlist (revlist); + return 0; + } + + /* 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); + + if (!is_rlog) + { + cvs_output ("\nWorking file: ", 0); + if (finfo->update_dir[0] != '\0') + { + 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->strict_locks) + cvs_output (" strict", 0); + walklist (RCS_getlocks (rcsfile), printlock_proc, NULL); + + cvs_output ("\naccess list:", 0); + if (rcsfile->access != NULL) + { + const char *cp; + + cp = rcsfile->access; + while (*cp != '\0') + { + const char *cp2; + + cvs_output ("\n\t", 2); + cp2 = cp; + while (!isspace ((unsigned char)*cp2) && *cp2 != '\0') + ++cp2; + cvs_output (cp, cp2 - cp); + cp = cp2; + while (isspace ((unsigned char)*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 (selrev >= 0) + { + cvs_output (";\tselected revisions: ", 0); + sprintf (buf, "%d", selrev); + cvs_output (buf, 0); + } + + cvs_output ("\n", 1); + + if (!log_data->header || log_data->long_header) + { + cvs_output ("description:\n", 0); + if (rcsfile->desc != NULL) + cvs_output (rcsfile->desc, 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 = 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 (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 = xmalloc (sizeof *nr); + nr->inclusive = r->inclusive; + + 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 ((unsigned char) r->first[0])) + nr->first = RCS_getbranch (rcs, r->first, 1); + else + { + branch = RCS_whatbranch (rcs, r->first); + if (branch == NULL) + nr->first = NULL; + else + { + nr->first = RCS_getbranch (rcs, branch, 1); + free (branch); + } + } + if (nr->first == NULL && !really_quiet) + { + error (0, 0, "warning: no branch `%s' in `%s'", + r->first, rcs->path); + nr->last = NULL; + nr->fields = 0; + } + else + { + nr->last = xstrdup (nr->first); + nr->fields = numdots (nr->first) + 1; + } + } + else + { + if (r->first == NULL || isdigit ((unsigned char) 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, NULL); + if (nr->first == NULL && !really_quiet) + { + error (0, 0, "warning: no revision `%s' in `%s'", + r->first, rcs->path); + } + } + + if (r->last == r->first || (r->last != NULL && r->first != NULL && + strcmp (r->last, r->first) == 0)) + nr->last = xstrdup (nr->first); + else if (r->last == NULL || isdigit ((unsigned char) 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, NULL); + if (nr->last == NULL && !really_quiet) + { + error (0, 0, "warning: no revision `%s' in `%s'", + r->last, rcs->path); + } + } + + /* 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->last != 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 + 1, "0"); + } + } + else if (r->last == NULL && nr->first != 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 if (nr->first == NULL || nr->last == NULL) + nr->fields = 0; + else if (strcmp (nr->first, nr->last) == 0) + nr->fields = numdots (nr->last) + 1; + else + { + int ord; + int dots1 = numdots (nr->first); + int dots2 = numdots (nr->last); + if (dots1 > dots2 || (dots1 == dots2 && + version_compare (nr->first, nr->last, dots1 + 1) > 0)) + { + char *tmp = nr->first; + nr->first = nr->last; + nr->last = tmp; + nr->fields = dots2 + 1; + dots2 = dots1; + dots1 = nr->fields - 1; + } + else + nr->fields = dots1 + 1; + dots1 += (nr->fields & 1); + ord = version_compare (nr->first, nr->last, dots1); + if (ord > 0 || (nr->fields > 2 && ord < 0)) + { + error (0, 0, + "invalid branch or revision pair %s:%s in `%s'", + r->first, r->last, rcs->path); + free (nr->first); + nr->first = NULL; + free (nr->last); + nr->last = NULL; + nr->fields = 0; + } + else + { + if (nr->fields <= dots2 && (nr->fields & 1)) + { + char *p = xmalloc (strlen (nr->first) + 3); + strcpy (p, nr->first); + strcat (p, ".0"); + free (nr->first); + nr->first = p; + ++nr->fields; + } + while (nr->fields <= dots2) + { + char *p; + int i; + + nr->next = NULL; + *pr = nr; + nr = xmalloc (sizeof *nr); + nr->inclusive = 1; + nr->first = xstrdup ((*pr)->last); + nr->last = xstrdup ((*pr)->last); + nr->fields = (*pr)->fields; + p = (*pr)->last; + for (i = 0; i < nr->fields; i++) + p = strchr (p, '.') + 1; + p[-1] = '\0'; + p = strchr (nr->first + (p - (*pr)->last), '.'); + if (p != NULL) + { + *++p = '0'; + *++p = '\0'; + nr->fields += 2; + } + else + ++nr->fields; + pr = &(*pr)->next; + } + } + } + } + + 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 = 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->inclusive = 1; + + nr->next = NULL; + *pr = nr; + } + + return ret; +} + + + +/* + * Free a revlist created by log_expand_revlist. + */ +static void +log_free_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 (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) && + (r->inclusive ? version_compare (v, r->first, r->fields) >= 0 : + 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 (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 (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 (Node *p, void *closure) +{ + struct log_data_and_rcs *data = 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 = 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 (Node *p, void *closure) +{ + struct log_data_and_rcs *data = 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, + 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 (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 = 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 (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 = 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 (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 (RCS_getlocks (rcs), ver->version); + if (p != NULL) + { + cvs_output ("\tlocked by: ", 0); + cvs_output (p->data, 0); + cvs_output (";", 1); + } + cvs_output ("\n", 1); + + cvs_output_tagged ("text", "date: "); + (void)sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min, + &sec); + if (year < 1900) + year += 1900; + sprintf (buf, "%04d-%02d-%02d %02d:%02d:%02d +0000", year, mon, mday, + hour, min, sec); + cvs_output_tagged ("date", buf); + + cvs_output_tagged ("text", "; author: "); + cvs_output_tagged ("text", ver->author); + + cvs_output_tagged ("text", "; state: "); + cvs_output_tagged ("text", ver->state); + cvs_output_tagged ("text", ";"); + + 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 = nextp->data; + pdel = findnode (nextver->other, ";add"); + padd = findnode (nextver->other, ";delete"); + } + + if (padd != NULL) + { + cvs_output_tagged ("text", " lines: +"); + cvs_output_tagged ("text", padd->data); + cvs_output_tagged ("text", " -"); + cvs_output_tagged ("text", pdel->data); + } + cvs_output_tagged ("newline", NULL); + + if (ver->branches != NULL) + { + cvs_output ("branches:", 0); + walklist (ver->branches, log_branch, NULL); + cvs_output ("\n", 1); + } + + p = findnode (ver->other, "log"); + /* The p->date == NULL case is the normal one for an empty log + message (rcs-14 in sanity.sh). I don't think the case where + p->data is "" can happen (getrcskey in rcs.c checks for an + empty string and set the value to NULL in that case). My guess + would be the p == NULL case would mean an RCS file which was + missing the "log" keyword (which is invalid according to + rcsfile.5). */ + if (p == NULL || p->data == NULL || *(char *)p->data == '\0') + cvs_output ("*** empty log message ***\n", 0); + else + { + /* FIXME: Technically, the log message could contain a null + byte. */ + cvs_output (p->data, 0); + if (((char *)p->data)[strlen (p->data) - 1] != '\n') + cvs_output ("\n", 1); + } +} + + + +/* + * Output a branch version. This is called via walklist. + */ +/*ARGSUSED*/ +static int +log_branch (Node *p, void *closure) +{ + cvs_output (" ", 2); + if ((numdots (p->key) & 1) == 0) + cvs_output (p->key, 0); + else + { + char *f, *cp; + + f = xstrdup (p->key); + cp = strrchr (f, '.'); + *cp = '\0'; + cvs_output (f, 0); + free (f); + } + cvs_output (";", 1); + return 0; +} + + + +/* + * Print a warm fuzzy message + */ +/* ARGSUSED */ +static Dtype +log_dirproc (void *callerdat, const char *dir, const char *repository, + const char *update_dir, List *entries) +{ + if (!isdir (dir)) + return R_SKIP_ALL; + + if (!quiet) + error (0, 0, "Logging %s", update_dir); + return R_PROCESS; +} + + + +/* + * Compare versions. This is taken from RCS compartial. + */ +static int +version_compare (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 ((unsigned char) v1[d1]); ++d1) + ; + + while (*v2 == '0') + ++v2; + for (d2 = 0; isdigit ((unsigned char) 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-1.12.9/src/login.c b/contrib/cvs-1.12.9/src/login.c new file mode 100644 index 0000000000..d111c8c7f1 --- /dev/null +++ b/contrib/cvs-1.12.9/src/login.c @@ -0,0 +1,652 @@ +/* + * Copyright (c) 1995, Cyclic Software, Bloomington, IN, USA + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with CVS. + * + * Allow user to log in for an authenticating server. + */ + +#include "cvs.h" +#include "getline.h" + +#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 CVS_PASSWORD_FILE +#define CVS_PASSWORD_FILE ".cvspass" +#endif + +/* If non-NULL, get_cvs_password() will just return this. */ +static char *cvs_password = NULL; + +static char *construct_cvspass_filename (void); + +/* The return value will need to be freed. */ +static char * +construct_cvspass_filename (void) +{ + char *homedir; + char *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) + { + /* FIXME? This message confuses a lot of users, at least + on Win95 (which doesn't set HOMEDRIVE and HOMEPATH like + NT does). I suppose the answer for Win95 is to store the + passwords in the registry or something (??). And .cvsrc + and such too? Wonder what WinCVS does (about .cvsrc, the + right thing for a GUI is to just store the password in + memory only)... */ + error (1, 0, "could not find out home directory"); + return (char *) NULL; + } + + passfile = strcat_filename_onto_homedir (homedir, CVS_PASSWORD_FILE); + + /* Safety first and last, Scouts. */ + if (isfile (passfile)) + /* xchmod() is too polite. */ + chmod (passfile, 0600); + + return passfile; +} + + + +/* + * static char * + * password_entry_parseline ( + * const char *cvsroot_canonical, + * const unsigned char warn, + * const int linenumber, + * char *linebuf + * ); + * + * Internal function used by password_entry_operation. Parse a single line + * from a ~/.cvsroot password file and return a pointer to the password if the + * line refers to the same cvsroot as cvsroot_canonical + * + * INPUTS + * cvsroot_canonical the root we are looking for + * warn Boolean: print warnings for invalid lines? + * linenumber the line number for error messages + * linebuf the current line + * + * RETURNS + * NULL if the line doesn't match + * char *password as a pointer into linebuf + * + * NOTES + * This function temporarily alters linebuf, so it isn't thread safe when + * called on the same linebuf + */ +static char * +password_entry_parseline (const char *cvsroot_canonical, + const unsigned char warn, const int linenumber, + char *linebuf) +{ + char *password = NULL; + char *p; + + /* look for '^/' */ + if (*linebuf == '/') + { + /* Yes: slurp '^/\d+\D' and parse the rest of the line according to version number */ + char *q; + unsigned long int entry_version; + + if (isspace(*(linebuf + 1))) + /* special case since strtoul ignores leading white space */ + entry_version = 0; + else + entry_version = strtoul (linebuf + 1, &q, 10); + + if (q == linebuf + 1) + /* no valid digits found by strtoul */ + entry_version = 0; + else + /* assume a delimiting seperator */ + q++; + + switch (entry_version) + { + case 1: + /* this means the same normalize_cvsroot we are using was + * used to create this entry. strcmp is good enough for + * us. + */ + p = strchr (q, ' '); + if (p == NULL) + { + if (warn && !really_quiet) + error (0, 0, "warning: skipping invalid entry in password file at line %d", + linenumber); + } + else + { + *p = '\0'; + if (strcmp (cvsroot_canonical, q) == 0) + password = p + 1; + *p = ' '; + } + break; + case ULONG_MAX: + if (warn && !really_quiet) + { + error (0, errno, "warning: unable to convert version number in password file at line %d", + linenumber); + error (0, 0, "skipping entry"); + } + break; + case 0: + if (warn && !really_quiet) + error (0, 0, "warning: skipping entry with invalid version string in password file at line %d", + linenumber); + break; + default: + if (warn && !really_quiet) + error (0, 0, "warning: skipping entry with unknown version (%lu) in password file at line %d", + entry_version, linenumber); + break; + } + } + else + { + /* No: assume: + * + * ^cvsroot Aencoded_password$ + * + * as header comment specifies and parse accordingly + */ + cvsroot_t *tmp_root; + char *tmp_root_canonical; + + p = strchr (linebuf, ' '); + if (p == NULL) + { + if (warn && !really_quiet) + error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber); + return NULL;; + } + + *p = '\0'; + if ((tmp_root = parse_cvsroot (linebuf)) == NULL) + { + if (warn && !really_quiet) + error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber); + *p = ' '; + return NULL; + } + *p = ' '; + tmp_root_canonical = normalize_cvsroot (tmp_root); + if (strcmp (cvsroot_canonical, tmp_root_canonical) == 0) + password = p + 1; + + free (tmp_root_canonical); + free_cvsroot_t (tmp_root); + } + + return password; +} + + + +/* + * static char * + * password_entry_operation ( + * password_entry_operation_t operation, + * cvsroot_t *root, + * char *newpassword + * ); + * + * Search the password file and depending on the value of operation: + * + * Mode Action + * password_entry_lookup Return the password + * password_entry_delete Delete the entry from the file, if it + * exists. + * password_entry_add Replace the line with the new one, else + * append it. + * + * Because the user might be accessing multiple repositories, with + * different passwords for each one, the format of ~/.cvspass is: + * + * [user@]host:[port]/path Aencoded_password + * [user@]host:[port]/path Aencoded_password + * ... + * + * New entries are always of the form: + * + * /1 user@host:port/path Aencoded_password + * + * but the old format is supported for backwards compatibility. + * The entry version string wasn't strictly necessary, but it avoids the + * overhead of parsing some entries since we know it is already in canonical + * form and allows room for expansion later, say, if we want to allow spaces + * and/or other characters to be escaped in the string. Also, the new entries + * would have been ignored by old versions of CVS anyhow since those versions + * didn't know how to parse a port number. + * + * The "A" before "encoded_password" is a literal capital A. It's a + * version number indicating which form of scrambling we're doing on + * the password -- someday we might provide something more secure than + * the trivial encoding we do now, and when that day comes, it would + * be nice to remain backward-compatible. + * + * Like .netrc, the file's permissions are the only thing preventing + * it from being read by others. Unlike .netrc, we will not be + * fascist about it, at most issuing a warning, and never refusing to + * work. + * + * INPUTS + * operation operation to perform + * root cvsroot_t to look up + * newpassword prescrambled new password, for password_entry_add_mode + * + * RETURNS + * -1 if password_entry_lookup_mode not specified + * NULL on failed lookup + * pointer to a copy of the password string otherwise, which the caller is + * responsible for disposing of + */ + +typedef enum password_entry_operation_e { + password_entry_lookup, + password_entry_delete, + password_entry_add +} password_entry_operation_t; + +static char * +password_entry_operation (password_entry_operation_t operation, cvsroot_t *root, char *newpassword) +{ + char *passfile; + FILE *fp; + char *cvsroot_canonical = NULL; + char *password = NULL; + int line_length; + long line = -1; + char *linebuf = NULL; + size_t linebuf_len; + char *p; + int save_errno = 0; + + if (root->method != pserver_method) + { + error (0, 0, "\ +internal error: can only call password_entry_operation with pserver method"); + error (1, 0, "CVSROOT: %s", root->original); + } + + cvsroot_canonical = normalize_cvsroot (root); + + /* Yes, the method below reads the user's password file twice when we have + * to delete an entry. It's inefficient, but we're not talking about a gig of + * data here. + */ + + passfile = construct_cvspass_filename (); + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) + { + error (0, errno, "warning: failed to open %s for reading", passfile); + goto process; + } + + /* Check each line to see if we have this entry already. */ + line = 0L; + while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + line++; + password = password_entry_parseline (cvsroot_canonical, 1, line, + linebuf); + if (password != NULL) + /* this is it! break out and deal with linebuf */ + break; + } + if (line_length < 0 && !feof (fp)) + { + error (0, errno, "cannot read %s", passfile); + goto error_exit; + } + if (fclose (fp) < 0) + /* not fatal, unless it cascades */ + error (0, errno, "cannot close %s", passfile); + fp = NULL; + + /* Utter, total, raving paranoia, I know. */ + chmod (passfile, 0600); + + /* a copy to return or keep around so we can reuse linebuf */ + if (password != NULL) + { + /* chomp the EOL */ + p = strchr (password, '\n'); + if (p != NULL) + *p = '\0'; + password = xstrdup (password); + } + +process: + + /* might as well return now */ + if (operation == password_entry_lookup) + goto out; + + /* same here */ + if (operation == password_entry_delete && password == NULL) + { + error (0, 0, "Entry not found."); + goto out; + } + + /* okay, file errors can simply be fatal from now on since we don't do + * anything else if we're in lookup mode + */ + + /* copy the file with the entry deleted unless we're in add + * mode and the line we found contains the same password we're supposed to + * add + */ + if (!noexec && password != NULL && (operation == password_entry_delete + || (operation == password_entry_add + && strcmp (password, newpassword)))) + { + long found_at = line; + char *tmp_name; + FILE *tmp_fp; + + /* open the original file again */ + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) + error (1, errno, "failed to open %s for reading", passfile); + + /* create and open a temp file */ + if ((tmp_fp = cvs_temp_file (&tmp_name)) == NULL) + error (1, errno, "unable to open temp file %s", tmp_name); + + line = 0L; + while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + line++; + if (line < found_at + || (line != found_at + && !password_entry_parseline (cvsroot_canonical, 0, line, + linebuf))) + { + if (fprintf (tmp_fp, "%s", linebuf) == EOF) + { + /* try and clean up anyhow */ + error (0, errno, "fatal error: cannot write %s", tmp_name); + if (fclose (tmp_fp) == EOF) + error (0, errno, "cannot close %s", tmp_name); + /* call CVS_UNLINK instead of unlink_file since the file + * got created in noexec mode + */ + if (CVS_UNLINK (tmp_name) < 0) + error (0, errno, "cannot remove %s", tmp_name); + /* but quit so we don't remove all the entries from a + * user's password file accidentally + */ + error (1, 0, "exiting"); + } + } + } + if (line_length < 0 && !feof (fp)) + { + error (0, errno, "cannot read %s", passfile); + goto error_exit; + } + if (fclose (fp) < 0) + /* not fatal, unless it cascades */ + error (0, errno, "cannot close %s", passfile); + if (fclose (tmp_fp) < 0) + /* not fatal, unless it cascades */ + /* FIXME - does copy_file return correct results if the file wasn't + * closed? should this be fatal? + */ + error (0, errno, "cannot close %s", tmp_name); + + /* FIXME: rename_file would make more sense (e.g. almost + * always faster). + * + * I don't think so, unless we change the way rename_file works to + * attempt a cp/rm sequence when rename fails since rename doesn't + * work across file systems and it isn't uncommon to have /tmp + * on its own partition. + * + * For that matter, it's probably not uncommon to have a home + * directory on an NFS mount. + */ + copy_file (tmp_name, passfile); + if (CVS_UNLINK (tmp_name) < 0) + error (0, errno, "cannot remove %s", tmp_name); + free (tmp_name); + } + + /* in add mode, if we didn't find an entry or found an entry with a + * different password, append the new line + */ + if (!noexec && operation == password_entry_add + && (password == NULL || strcmp (password, newpassword))) + { + if ((fp = CVS_FOPEN (passfile, "a")) == NULL) + error (1, errno, "could not open %s for writing", passfile); + + if (fprintf (fp, "/1 %s %s\n", cvsroot_canonical, newpassword) == EOF) + error (1, errno, "cannot write %s", passfile); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", passfile); + } + + /* Utter, total, raving paranoia, I know. */ + chmod (passfile, 0600); + + if (password) + { + free (password); + password = NULL; + } + if (linebuf) + free (linebuf); + +out: + free (cvsroot_canonical); + free (passfile); + return password; + +error_exit: + /* just exit when we're not in lookup mode */ + if (operation != password_entry_lookup) + error (1, 0, "fatal error: exiting"); + /* clean up and exit in lookup mode so we can try a login with a NULL + * password anyhow in case that's what we would have found + */ + save_errno = errno; + if (fp != NULL) + { + /* Utter, total, raving paranoia, I know. */ + chmod (passfile, 0600); + if(fclose (fp) < 0) + error (0, errno, "cannot close %s", passfile); + } + if (linebuf) + free (linebuf); + if (cvsroot_canonical) + free (cvsroot_canonical); + free (passfile); + errno = save_errno; + return NULL; +} + + + +/* Prompt for a password, and store it in the file "CVS/.cvspass". + */ + +static const char *const login_usage[] = +{ + "Usage: %s %s\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +login (int argc, char **argv) +{ + char *typed_password; + char *cvsroot_canonical; + + if (argc < 0) + usage (login_usage); + + if (current_parsed_root->method != pserver_method) + { + error (0, 0, "can only use `login' command with the 'pserver' method"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); + } + + cvsroot_canonical = normalize_cvsroot(current_parsed_root); + printf ("Logging in to %s\n", cvsroot_canonical); + fflush (stdout); + + if (current_parsed_root->password) + { + typed_password = scramble (current_parsed_root->password); + } + else + { + char *tmp; + tmp = getpass ("CVS password: "); + /* Must deal with a NULL return value here. I haven't managed to + * disconnect the CVS process from the tty and force a NULL return + * in sanity.sh, but the Linux version of getpass is documented + * to return NULL when it can't open /dev/tty... + */ + if (!tmp) error (1, errno, "login: Failed to read password."); + typed_password = scramble (tmp); + memset (tmp, 0, strlen (tmp)); + } + + /* 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); + + connect_to_pserver (current_parsed_root, NULL, NULL, 1, 0); + + password_entry_operation (password_entry_add, current_parsed_root, + typed_password); + + memset (typed_password, 0, strlen (typed_password)); + free (typed_password); + + free (cvs_password); + free (cvsroot_canonical); + cvs_password = NULL; + + return 0; +} + + + +/* Returns the _scrambled_ password. The server must descramble + before hashing and comparing. If password file not found, or + password not found in the file, just return NULL. */ +char * +get_cvs_password (void) +{ + if (current_parsed_root->password) + return scramble (current_parsed_root->password); + + /* 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"); + } + + if (current_parsed_root->method != pserver_method) + { + error (0, 0, "can only call get_cvs_password with pserver method"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); + } + + return password_entry_operation (password_entry_lookup, + current_parsed_root, NULL); +} + + + +static const char *const logout_usage[] = +{ + "Usage: %s %s\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +/* Remove any entry for the CVSRoot repository found in .cvspass. */ +int +logout (int argc, char **argv) +{ + char *cvsroot_canonical; + + if (argc < 0) + usage (logout_usage); + + if (current_parsed_root->method != pserver_method) + { + error (0, 0, "can only use pserver method with `logout' command"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); + } + + /* 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. */ + + if (!quiet) + { + cvsroot_canonical = normalize_cvsroot(current_parsed_root); + printf ("Logging out of %s\n", cvsroot_canonical); + fflush (stdout); + free (cvsroot_canonical); + } + + password_entry_operation (password_entry_delete, current_parsed_root, NULL); + + return 0; +} + +#endif /* AUTH_CLIENT_SUPPORT from beginning of file. */ diff --git a/contrib/cvs-1.12.9/src/logmsg.c b/contrib/cvs-1.12.9/src/logmsg.c new file mode 100644 index 0000000000..53991fa4f2 --- /dev/null +++ b/contrib/cvs-1.12.9/src/logmsg.c @@ -0,0 +1,961 @@ +/* + * 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 source distribution. + */ + + +#include "cvs.h" +#include "getline.h" + +static int find_type (Node * p, void *closure); +static int fmt_proc (Node * p, void *closure); +static int logfile_write (const char *repository, const char *filter, + const char *message, FILE * logfp, List * changes); +static int logmsg_list_to_args_proc (Node *p, void *closure); +static int rcsinfo_proc (const char *repository, const char *template, + void *closure ); +static int update_logfile_proc (const char *repository, const char *filter, + void *closure); +static void setup_tmpfile (FILE * xfp, char *xprefix, List * changes); +static int verifymsg_proc (const char *repository, const char *script, + void *closure ); + +static FILE *fp; +static Ctype type; + +struct verifymsg_proc_data +{ + /* The name of the temp file storing the log message to be verified. This + * is initially NULL and verifymsg_proc() writes message into it so that it + * can be shared when multiple verifymsg scripts exist. do_verify() is + * responsible for rereading the message from the file when + * RereadLogAfterVerify is in effect and the file has changed. + */ + char *fname; + /* The initial message text to be verified. + */ + char *message; + /* The initial stats of the temp file so we can tell that the temp file has + * been changed when RereadLogAfterVerify is STAT. + */ + struct stat pre_stbuf; +}; + +/* + * Should the logmsg be re-read during the do_verify phase? + * RereadLogAfterVerify=no|stat|yes + * LOGMSG_REREAD_NEVER - never re-read the logmsg + * LOGMSG_REREAD_STAT - re-read the logmsg only if it has changed + * LOGMSG_REREAD_ALWAYS - always re-read the logmsg + */ +int RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS; + +/* + * Puts a standard header on the output which is either being prepared for an + * editor session, or being sent to a logfile program. The modified, added, + * and removed files are included (if any) and formatted to look pretty. */ +static char *prefix; +static int col; +static char *tag; +static void +setup_tmpfile (FILE *xfp, char *xprefix, List *changes) +{ + /* set up statics */ + fp = xfp; + prefix = xprefix; + + type = T_MODIFIED; + if (walklist (changes, find_type, NULL) != 0) + { + (void) fprintf (fp, "%sModified Files:\n", prefix); + 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); + 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); + col = 0; + (void) walklist (changes, fmt_proc, NULL); + (void) fprintf (fp, "\n"); + if (tag != NULL) + { + free (tag); + tag = NULL; + } + } +} + +/* + * Looks for nodes of a specified type and returns 1 if found + */ +static int +find_type (Node *p, void *closure) +{ + struct logfile_info *li = p->data; + + if (li->type == type) + return (1); + else + return (0); +} + +/* + * Breaks the files list into reasonable sized lines to avoid line wrap... + * all in the name of pretty output. It only works on nodes whose types + * match the one we're looking for + */ +static int +fmt_proc (Node *p, void *closure) +{ + struct logfile_info *li; + + li = p->data; + if (li->type == type) + { + if (li->tag == NULL + ? tag != NULL + : tag == NULL || strcmp (tag, li->tag) != 0) + { + if (col > 0) + (void) fprintf (fp, "\n"); + (void) fputs (prefix, fp); + 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; + } + (void) fprintf (fp, "%s ", p->key); + col += strlen (p->key) + 1; + } + return (0); +} + +/* + * Builds a temporary file using setup_tmpfile() and invokes the user's + * editor on the file. The header garbage in the resultant file is then + * stripped and the log message is stored in the "message" argument. + * + * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it + * is NULL, use the CVSADM_TEMPLATE file instead. REPOSITORY should be + * NULL when running in client mode. + * + * GLOBALS + * Editor Set to a default value by configure and overridable using the + * -e option to the CVS executable. + */ +void +do_editor (const char *dir, char **messagep, const char *repository, + List *changes) +{ + static int reuse_log_message = 0; + char *line; + int line_length; + size_t line_chars_allocated; + char *fname; + struct stat pre_stbuf, post_stbuf; + int retcode = 0; + +#ifdef CLIENT_SUPPORT + assert (!current_parsed_root->isremote != !repository); +#else + assert (repository); +#endif + + if (noexec || reuse_log_message) + return; + + /* Abort before creation of the temp file if no editor is defined. */ + if (strcmp (Editor, "") == 0) + error(1, 0, "no editor defined, must use -e or -m"); + + again: + /* Create a temporary file. */ + if( ( fp = cvs_temp_file( &fname ) ) == NULL ) + error( 1, errno, "cannot create temporary file" ); + + if (*messagep) + { + (void) fputs (*messagep, fp); + + if ((*messagep)[0] == '\0' || + (*messagep)[strlen (*messagep) - 1] != '\n') + (void) fprintf (fp, "\n"); + } + + if (repository != NULL) + /* tack templates on if necessary */ + (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, + PIOPT_ALL, NULL); + else + { + FILE *tfp; + char buf[1024]; + size_t n; + size_t nwrite; + + /* Why "b"? */ + tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb"); + if (tfp == NULL) + { + if (!existence_error (errno)) + error (1, errno, "cannot read %s", CVSADM_TEMPLATE); + } + else + { + while (!feof (tfp)) + { + char *p = buf; + n = fread (buf, 1, sizeof buf, tfp); + nwrite = n; + while (nwrite > 0) + { + n = fwrite (p, 1, nwrite, fp); + nwrite -= n; + p += n; + } + if (ferror (tfp)) + error (1, errno, "cannot read %s", CVSADM_TEMPLATE); + } + if (fclose (tfp) < 0) + error (0, errno, "cannot close %s", CVSADM_TEMPLATE); + } + } + + (void) fprintf (fp, + "%s----------------------------------------------------------------------\n", + CVSEDITPREFIX); + (void) fprintf (fp, + "%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); + if (changes != NULL) + setup_tmpfile (fp, CVSEDITPREFIX, changes); + (void) fprintf (fp, + "%s----------------------------------------------------------------------\n", + CVSEDITPREFIX); + + /* finish off the temp file */ + if (fclose (fp) == EOF) + error (1, errno, "%s", fname); + if ( CVS_STAT (fname, &pre_stbuf) == -1) + pre_stbuf.st_mtime = 0; + + /* run the editor */ + run_setup (Editor); + run_arg (fname); + if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, + RUN_NORMAL | RUN_SIGIGNORE)) != 0) + error (0, retcode == -1 ? errno : 0, "warning: editor session failed"); + + /* put the entire message back into the *messagep variable */ + + fp = open_file (fname, "r"); + + if (*messagep) + free (*messagep); + + if ( CVS_STAT (fname, &post_stbuf) != 0) + error (1, errno, "cannot find size of temp file %s", fname); + + if (post_stbuf.st_size == 0) + *messagep = NULL; + else + { + /* On NT, we might read less than st_size bytes, but we won't + read more. So this works. */ + *messagep = (char *) xmalloc (post_stbuf.st_size + 1); + (*messagep)[0] = '\0'; + } + + line = NULL; + line_chars_allocated = 0; + + if (*messagep) + { + size_t message_len = post_stbuf.st_size + 1; + size_t offset = 0; + while (1) + { + line_length = getline (&line, &line_chars_allocated, fp); + if (line_length == -1) + { + if (ferror (fp)) + error (0, errno, "warning: cannot read %s", fname); + break; + } + if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) + continue; + if (offset + line_length >= message_len) + expand_string (messagep, &message_len, + offset + line_length + 1); + (void) strcpy (*messagep + offset, line); + offset += line_length; + } + } + if (fclose (fp) < 0) + error (0, errno, "warning: cannot close %s", fname); + + /* canonicalize emply messages */ + if (*messagep != NULL && + (**messagep == '\0' || strcmp (*messagep, "\n") == 0)) + { + free (*messagep); + *messagep = NULL; + } + + if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL) + { + for (;;) + { + (void) printf ("\nLog message unchanged or not specified\n"); + (void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n"); + (void) printf ("Action: (continue) "); + (void) fflush (stdout); + line_length = getline (&line, &line_chars_allocated, stdin); + if (line_length < 0) + { + error (0, errno, "cannot read from stdin"); + if (unlink_file (fname) < 0) + error (0, errno, + "warning: cannot remove temp file %s", fname); + error (1, 0, "aborting"); + } + else if (line_length == 0 + || *line == '\n' || *line == 'c' || *line == 'C') + break; + if (*line == 'a' || *line == 'A') + { + 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 == '!') + { + reuse_log_message = 1; + break; + } + (void) printf ("Unknown input\n"); + } + } + if (line) + 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 attribute. unlike the do_editor function, this is + independant of the running of an editor for getting a message. + */ +void +do_verify (char **messagep, const char *repository) +{ + int err; + struct verifymsg_proc_data data; + struct stat post_stbuf; + +#ifdef CLIENT_SUPPORT + if( current_parsed_root->isremote ) + /* 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? */ + /* EXPLAIN: Why do we check for repository == NULL here? */ + if (noexec || repository == NULL) + return; + + /* Get the name of the verification script to run */ + + data.message = *messagep; + data.fname = NULL; + if ((err = Parse_Info (CVSROOTADM_VERIFYMSG, repository, + verifymsg_proc, 0, &data)) != 0) + { + int saved_errno = errno; + /* Since following error() exits, delete the temp file now. */ + if (data.fname != NULL && unlink_file( data.fname ) < 0) + error (0, errno, "cannot remove %s", data.fname); + free (data.fname); + + errno = saved_errno; + error (1, err == -1 ? errno : 0, "Message verification failed"); + } + + /* Return if no temp file was created. That means that we didn't call any + * verifymsg scripts. + */ + if (data.fname == NULL) + return; + + /* Get the mod time and size of the possibly new log message + * in always and stat modes. + */ + if (RereadLogAfterVerify == LOGMSG_REREAD_ALWAYS || + RereadLogAfterVerify == LOGMSG_REREAD_STAT) + { + if(CVS_STAT (data.fname, &post_stbuf) != 0) + error (1, errno, "cannot find size of temp file %s", data.fname); + } + + /* And reread the log message in `always' mode or in `stat' mode when it's + * changed. + */ + if (RereadLogAfterVerify == LOGMSG_REREAD_ALWAYS || + (RereadLogAfterVerify == LOGMSG_REREAD_STAT && + (data.pre_stbuf.st_mtime != post_stbuf.st_mtime || + data.pre_stbuf.st_size != post_stbuf.st_size))) + { + /* put the entire message back into the *messagep variable */ + + if (*messagep) free (*messagep); + + if (post_stbuf.st_size == 0) + *messagep = NULL; + else + { + char *line = NULL; + int line_length; + size_t line_chars_allocated = 0; + char *p; + FILE *fp; + + if ((fp = open_file (data.fname, "r")) == NULL) + error (1, errno, "cannot open temporary file %s", data.fname); + + /* On NT, we might read less than st_size bytes, + but we won't read more. So this works. */ + p = *messagep = (char *) xmalloc (post_stbuf.st_size + 1); + *messagep[0] = '\0'; + + for (;;) + { + line_length = getline( &line, + &line_chars_allocated, + fp); + if (line_length == -1) + { + if (ferror (fp)) + /* Fail in this case because otherwise we will have no + * log message + */ + error (1, errno, "cannot read %s", data.fname); + break; + } + if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) + continue; + (void) strcpy (p, line); + p += line_length; + } + if (line) free (line); + if (fclose (fp) < 0) + error (0, errno, "warning: cannot close %s", data.fname); + } + } + /* Delete the temp file */ + if (unlink_file (data.fname) < 0) + error (0, errno, "cannot remove `%s'", data.fname); + free (data.fname); +} + + + +/* + * callback proc for Parse_Info for rcsinfo templates this routine basically + * copies the matching template onto the end of the tempfile we are setting + * up + */ +/* ARGSUSED */ +static int +rcsinfo_proc (const char *repository, const char *template, void *closure) +{ + static char *last_template; + FILE *tfp; + + /* nothing to do if the last one included is the same as this one */ + if (last_template && strcmp (last_template, template) == 0) + return (0); + if (last_template) + free (last_template); + last_template = xstrdup (template); + + if ((tfp = CVS_FOPEN (template, "r")) != NULL) + { + char *line = NULL; + size_t line_chars_allocated = 0; + + while (getline (&line, &line_chars_allocated, tfp) >= 0) + (void) fputs (line, fp); + if (ferror (tfp)) + error (0, errno, "warning: cannot read %s", template); + if (fclose (tfp) < 0) + error (0, errno, "warning: cannot close %s", template); + if (line) + free (line); + return (0); + } + else + { + error (0, errno, "Couldn't open rcsinfo template file %s", template); + return (1); + } +} + +/* + * Uses setup_tmpfile() to pass the updated message on directly to any + * logfile programs that have a regular expression match for the checked in + * directory in the source repository. The log information is fed into the + * specified program as standard input. + */ +struct ulp_data { + FILE *logfp; + const char *message; + List *changes; +}; + + + +void +Update_Logfile (const char *repository, const char *xmessage, FILE *xlogfp, + List *xchanges) +{ + struct ulp_data ud; + + /* nothing to do if the list is empty */ + if (xchanges == NULL || xchanges->list->next == xchanges->list) + return; + + /* set up vars for update_logfile_proc */ + ud.message = xmessage; + ud.logfp = xlogfp; + ud.changes = xchanges; + + /* call Parse_Info to do the actual logfile updates */ + (void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc, + PIOPT_ALL, &ud); +} + + + +/* + * callback proc to actually do the logfile write from Update_Logfile + */ +static int +update_logfile_proc (const char *repository, const char *filter, void *closure) +{ + struct ulp_data *udp = (struct ulp_data *)closure; + TRACE (TRACE_FUNCTION, "update_logfile_proc(%s,%s)", repository, filter); + return logfile_write (repository, filter, udp->message, udp->logfp, + udp->changes); +} + + + +/* static int + * logmsg_list_to_args_proc( Node *p, void *closure ) + * This function is intended to be passed into walklist() with a list of tags + * (nodes in the same format as pretag_list_proc() accepts - p->key = tagname + * and p->data = a revision. + * + * closure will be a struct format_cmdline_walklist_closure + * where closure is undefined. + */ +static int +logmsg_list_to_args_proc (Node *p, void *closure) +{ + struct format_cmdline_walklist_closure *c = closure; + struct logfile_info *li; + char *arg = NULL; + const char *f; + char *d; + size_t doff; + + if (p->data == NULL) return 1; + + f = c->format; + d = *c->d; + /* foreach requested attribute */ + while (*f) + { + switch (*f++) + { + case 's': + arg = p->key; + break; + case 'V': + li = p->data; + arg = li->rev_old ? li->rev_old : "NONE"; + break; + case 'v': + li = p->data; + arg = li->rev_new ? li->rev_new : "NONE"; + break; + default: +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (c->onearg) + { + /* The old deafult was to print the empty string for + * unknown args. + */ + arg = "\0"; + } + else +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + error (1, 0, + "Unknown format character or not a list attribute: %c", f[-1]); + /* NOTREACHED */ + break; + } + /* copy the attribute into an argument */ +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (c->onearg) + { + if (c->firstpass) + { + c->firstpass = 0; + doff = d - *c->buf; + expand_string (c->buf, c->length, + doff + strlen (c->srepos) + 1); + d = *c->buf + doff; + strncpy (d, c->srepos, strlen (c->srepos)); + d += strlen (c->srepos); + *d++ = ' '; + } + } + else /* c->onearg */ +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + { + if (c->quotes) + { + arg = cmdlineescape (c->quotes, arg); + } + else + { + arg = cmdlinequote ('"', arg); + } + } /* !c->onearg */ + doff = d - *c->buf; + expand_string (c->buf, c->length, doff + strlen (arg)); + d = *c->buf + doff; + strncpy (d, arg, strlen (arg)); + d += strlen (arg); +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (!c->onearg) +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + free (arg); + + /* Always put the extra space on. we'll have to back up a char + * when we're done, but that seems most efficient. + */ + doff = d - *c->buf; + expand_string (c->buf, c->length, doff + 1); + d = *c->buf + doff; +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (c->onearg && *f) *d++ = ','; + else +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + *d++ = ' '; + } + /* correct our original pointer into the buff */ + *c->d = d; + return 0; +} + + + +/* + * Writes some stuff to the logfile "filter" and returns the status of the + * filter program. + */ +static int +logfile_write (const char *repository, const char *filter, const char *message, + FILE *logfp, List *changes) +{ + char *cmdline; + FILE *pipefp; + char *cp; + int c; + int pipestatus; + const char *srepos = Short_Repository (repository); + + /* 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: + + ... + + 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. */ + + /* %p = shortrepos + * %r = repository + * %{sVv} = file name, old revision (precommit), new revision (postcommit) + */ + cmdline = format_cmdline( +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + !UseNewInfoFmtStrings, srepos, +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + filter, + "p", "s", srepos, + "r", "s", current_parsed_root->directory, + "sVv", ",", changes, + logmsg_list_to_args_proc, (void *) NULL, + (char *)NULL + ); + if( !cmdline || !strlen( cmdline ) ) + { + if( cmdline ) free( cmdline ); + error( 0, 0, "logmsg proc resolved to the empty string!" ); + return 1; + } + + if( ( pipefp = run_popen( cmdline, "w" ) ) == NULL ) + { + if (!noexec) + error( 0, 0, "cannot write entry to log filter: %s", cmdline ); + free( cmdline ); + return 1; + } + (void) fprintf (pipefp, "Update of %s\n", repository); + (void) fprintf (pipefp, "In directory %s:", hostname); + cp = xgetwd (); + if (cp == NULL) + fprintf (pipefp, "\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) ? message : ""); + if (logfp != (FILE *) 0) + { + (void) fprintf (pipefp, "Status:\n"); + rewind (logfp); + while ((c = getc (logfp)) != EOF) + (void) putc ((char) c, pipefp); + } + free (cmdline); + pipestatus = pclose (pipefp); + return ((pipestatus == -1) || (pipestatus == 127)) ? 1 : 0; +} + + + +/* This routine is calld by Parse_Info. It runs the + * message verification script. + */ +static int +verifymsg_proc (const char *repository, const char *script, void *closure) +{ + char *verifymsg_script; +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + char *newscript = NULL; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + struct verifymsg_proc_data *vpd = (struct verifymsg_proc_data *) closure; + const char *srepos = Short_Repository (repository); + +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (!strchr (script, '%')) + { + error (0, 0, + "warning: verifymsg line doesn't contain any format strings:\n" + " \"%s\"\n" + "Appending default format string (\" %%l\"), but be aware that this usage is\n" + "deprecated.", script); + newscript = xmalloc (strlen (script) + 4); + strcpy (newscript, script); + strcat (newscript, " %l"); + script = newscript; + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + + /* If we don't already have one, open a temporary file, write the message + * to the temp file, and close the file. + * + * We do this here so that we only create the file when there is a + * verifymsg script specified and we only create it once when there is + * more than one verifymsg script specified. + */ + if (vpd->fname == NULL) + { + FILE *fp; + if ((fp = cvs_temp_file (&(vpd->fname))) == NULL) + error (1, errno, "cannot create temporary file %s", vpd->fname); + + if (vpd->message != NULL) + fputs (vpd->message, fp); + if (vpd->message == NULL || + (vpd->message)[0] == '\0' || + (vpd->message)[strlen (vpd->message) - 1] != '\n') + putc ('\n', fp); + if (fclose (fp) == EOF) + error (1, errno, "%s", vpd->fname); + + if (RereadLogAfterVerify == LOGMSG_REREAD_STAT) + { + /* Remember the status of the temp file for later */ + if (CVS_STAT (vpd->fname, &(vpd->pre_stbuf)) != 0) + error (1, errno, "cannot stat temp file %s", vpd->fname); + + /* + * See if we need to sleep before running the verification + * script to avoid time-stamp races. + */ + sleep_past (vpd->pre_stbuf.st_mtime); + } + } /* if( vpd->fname == NULL ) */ + + verifymsg_script = format_cmdline ( +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + 0, srepos, +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + script, + "p", "s", srepos, + "r", "s", + current_parsed_root->directory, + "l", "s", vpd->fname, + (char *)NULL + ); + +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (newscript) free (newscript); +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + + if (!verifymsg_script || !strlen (verifymsg_script)) + { + if (verifymsg_script) free (verifymsg_script); + verifymsg_script = NULL; + error (0, 0, "verifymsg proc resolved to the empty string!"); + return 1; + } + + run_setup (verifymsg_script); + + free (verifymsg_script); + + /* FIXME - because run_exec can return negative values and Parse_Info adds + * the values of each call to this function to get a total error, we are + * calling abs on the value of run_exec to ensure two errors do not sum to + * zero. + * + * The only REALLY obnoxious thing about this, I guess, is that a -1 return + * code from run_exec can mean we failed to call the process for some + * reason and should care about errno or that the process we called + * returned -1 and the value of errno is undefined. In other words, + * run_exec should probably be rewritten to have two return codes. one + * which is it's own exit status and one which is the child process's. So + * there. :P + * + * Once run_exec is returning two error codes, we should probably be + * failing here with an error message including errno when we get the + * return code which means we care about errno, in case you missed that + * little tidbit. + * + * I do happen to know we just fail for a non-zero value anyway and I + * believe the docs actually state that if the verifymsg_proc returns a + * "non-zero" value we will fail. + */ + return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, + RUN_NORMAL | RUN_SIGIGNORE)); +} diff --git a/contrib/cvs-1.12.9/src/ls.c b/contrib/cvs-1.12.9/src/ls.c new file mode 100644 index 0000000000..1160d62cdb --- /dev/null +++ b/contrib/cvs-1.12.9/src/ls.c @@ -0,0 +1,669 @@ +/* + * Copyright (c) 1992, Brian Berliner and Jeff Polk + * Copyright (c) 1989-1992, Brian Berliner + * Copyright (c) 2001, Tony Hoyle + * Copyright (c) 2004, Derek R. Price & Ximbiot + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * Query CVS/Entries from server + */ + +#include "cvs.h" +#include + +static int ls_proc (int argc, char **argv, char *xwhere, char *mwhere, + char *mfile, int shorten, int local, char *mname, + char *msg); + +static RCSNode *xrcsnode; + +static const char *const ls_usage[] = +{ + "Usage: %s %s [-e | -l] [-RP] [-r rev] [-D date] [path...]\n", + "\t-d\tShow dead revisions (with tag when specified).\n", + "\t-e\tDisplay in CVS/Entries format.\n", + "\t-l\tDisplay all details.\n", + "\t-P\tPrune empty directories.\n", + "\t-R\tList recursively.\n", + "\t-r rev\tShow files with revision or tag.\n", + "\t-D date\tShow files from date.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +static bool entries_format; +static bool long_format; +static char *show_tag; +static char *show_date; +static bool set_tag; +static char *created_dir; +static bool tag_validated; +static bool recurse; +static bool ls_prune_dirs; +static char *regexp_match; +static bool is_rls; +static bool show_dead_revs; + + + +int +ls (int argc, char **argv) +{ + int c; + int err = 0; + + is_rls = strcmp (cvs_cmd_name, "rls") == 0; + + if (argc == -1) + usage (ls_usage); + + entries_format = false; + long_format = false; + show_tag = NULL; + show_date = NULL; + tag_validated = false; + recurse = false; + ls_prune_dirs = false; + show_dead_revs = false; + + optind = 0; + + while ((c = getopt (argc, argv, +#ifdef SERVER_SUPPORT + server_active ? "qdelr:D:PR" : +#endif /* SERVER_SUPPORT */ + "delr:D:RP" + )) != -1) + { + switch (c) + { +#ifdef SERVER_SUPPORT + case 'q': + if (server_active) + { + error (0, 0, +"`%s ls -q' is deprecated. Please use the global `-q' option instead.", + program_name); + quiet = true; + } + else + usage (ls_usage); + break; +#endif /* SERVER_SUPPORT */ + case 'd': + show_dead_revs = true; + break; + case 'e': + entries_format = true; + break; + case 'l': + long_format = true; + break; + case 'r': + show_tag = optarg; + break; + case 'D': + show_date = Make_Date (optarg); + break; + case 'P': + ls_prune_dirs = true; + break; + case 'R': + recurse = true; + break; + case '?': + default: + usage (ls_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (entries_format && long_format) + { + error (0, 0, "`-e' & `-l' are mutually exclusive."); + usage (ls_usage); + } + + wrap_setup (); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + /* We're the local client. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (is_rls ? !(supported_request ("rlist") || supported_request ("ls")) + : !supported_request ("list")) + error (1, 0, "server does not support %s", cvs_cmd_name); + + if (quiet && !supported_request ("global-list-quiet")) + send_arg ("-q"); + if (entries_format) + send_arg ("-e"); + if (long_format) + send_arg ("-l"); + if (ls_prune_dirs) + send_arg ("-P"); + if (recurse) + send_arg ("-R"); + if (show_dead_revs) + send_arg ("-d"); + if (show_tag) + option_with_arg ("-r", show_tag); + if (show_date) + client_senddate (show_date); + + send_arg ("--"); + + if (is_rls) + { + int i; + for (i = 0; i < argc; i++) + send_arg (argv[i]); + if (supported_request ("rlist")) + send_to_server ("rlist\012", 0); + else + /* For backwards compatibility with CVSNT... */ + send_to_server ("ls\012", 0); + } + else + { + /* Setting this means, I think, that any empty directories created + * by the server will be deleted by the client. Since any dirs + * created at all by ls should remain empty, this should cause any + * dirs created by the server for the ls command to be deleted. + */ + client_prune_dirs = 1; + + /* I explicitly decide not to send contents here. We *could* let + * the user pull status information with this command, but why + * don't they just use update or status? + */ + send_files (argc, argv, !recurse, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server ("list\012", 0); + } + + err = get_responses_and_close (); + return err; + } +#endif + + if (is_rls) + { + DBM *db; + int i; + db = open_module (); + if (argc) + { + for (i = 0; i < argc; i++) + { + char *mod = xstrdup (argv[i]); + char *p; + + for (p=strchr (mod,'\\'); p; p=strchr (p,'\\')) + *p='/'; + + p = strrchr (mod,'/'); + if (p && (strchr (p,'?') || strchr (p,'*'))) + { + *p='\0'; + regexp_match = p+1; + } + else + regexp_match = NULL; + + /* Frontends like to do 'ls -q /', so we support it explicitly. + */ + if (!strcmp (mod,"/")) + { + *mod='\0'; + } + + err += do_module (db, mod, MISC, "Listing", + ls_proc, NULL, 0, 0, 0, 0, NULL); + + free (mod); + } + } + else + { + /* should be ".", but do_recursion() + fails this: assert ( strstr ( repository, "/./" ) == NULL ); */ + char *topmod = xstrdup (""); + err += do_module (db, topmod, MISC, "Listing", + ls_proc, NULL, 0, 0, 0, 0, NULL); + free (topmod); + } + close_module (db); + } + else + ls_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, 0, NULL, NULL); + + return err; +} + + + +struct long_format_data +{ + char *header; + char *time; + char *footer; +}; + +static int +ls_print (Node *p, void *closure) +{ + if (long_format) + { + struct long_format_data *data = p->data; + cvs_output_tagged ("text", data->header); + if (data->time) + cvs_output_tagged ("date", data->time); + if (data->footer) + cvs_output_tagged ("text", data->footer); + cvs_output_tagged ("newline", NULL); + } + else + cvs_output (p->data, 0); + return 0; +} + + + +static int +ls_print_dir (Node *p, void *closure) +{ + static bool printed = false; + + if (recurse && !(ls_prune_dirs && list_isempty (p->data))) + { + /* Keep track of whether we've printed. If we have, then put a blank + * line before directory headers, to separate the header from the + * listing of the previous directory. + */ + if (printed) + cvs_output ("\n", 1); + else + printed = true; + + if (!strcmp (p->key, "")) + cvs_output (".", 1); + else + cvs_output (p->key, 0); + cvs_output (":\n", 2); + } + walklist (p->data, ls_print, NULL); + return 0; +} + + + +/* + * Delproc for a node containing a struct long_format_data as data. + */ +static void +long_format_data_delproc (Node *n) +{ + struct long_format_data *data = (struct long_format_data *)n->data; + if (data->header) free (data->header); + if (data->time) free (data->time); + if (data->footer) free (data->footer); + free (data); +} + + + +/* + * Add a file to our list of data to print for a directory. + */ +/* ARGSUSED */ +static int +ls_fileproc (void *callerdat, struct file_info *finfo) +{ + Vers_TS *vers; + char *regex_err; + void *buf; + size_t length; + Node *p, *n; + bool isdead; + + if (regexp_match) + { +#ifdef FILENAMES_CASE_INSENSITIVE + re_set_syntax (REG_ICASE|RE_SYNTAX_EGREP); +#else + re_set_syntax (RE_SYNTAX_EGREP); +#endif + if ((regex_err = re_comp (regexp_match)) != NULL) + { + error (1, 0, "bad regular expression passed to 'ls': %s", + regex_err); + } + if (re_exec (finfo->file) == 0) + return 0; /* no match */ + } + + vers = Version_TS (finfo, NULL, show_tag, show_date, 1, 0); + /* Skip dead revisions unless specifically requested to do otherwise. + * We also bother to check for long_format so we can print the state. + */ + if (vers->vn_rcs && (!show_dead_revs || long_format)) + isdead = RCS_isdead (finfo->rcs, vers->vn_rcs); + if (!vers->vn_rcs || !show_dead_revs && isdead) + { + freevers_ts (&vers); + return 0; + } + + n = getnode(); + if (entries_format) + { + char *outdate = entries_time (RCS_getrevtime (finfo->rcs, vers->vn_rcs, + 0, 0)); + n->data = asnprintf (NULL, &length, "/%s/%s/%s/%s/%s%s\n", + finfo->file, vers->vn_rcs, + outdate, vers->options, + show_tag ? "T" : "", show_tag ? show_tag : ""); + free (outdate); + } + else if (long_format) + { + struct long_format_data *out = + xmalloc (sizeof (struct long_format_data)); + out->header = asnprintf (NULL, &length, "%-5.5s", + vers->options[0] != '\0' ? vers->options + : "----"); + /* FIXME: Do we want to mimc the real `ls' command's date format? */ + out->time = gmformat_time_t (RCS_getrevtime (finfo->rcs, vers->vn_rcs, + 0, 0)); + out->footer = asnprintf (NULL, &length, " %-9.9s%s %s%s", + vers->vn_rcs, + strlen (vers->vn_rcs) > 9 ? "+" : " ", + show_dead_revs ? (isdead ? "dead " : " ") + : "", + finfo->file); + n->data = out; + n->delproc = long_format_data_delproc; + } + else + n->data = asnprintf (NULL, &length, "%s\n", finfo->file); + + p = findnode (callerdat, finfo->update_dir); + assert (p); + addnode (p->data, n); + + freevers_ts (&vers); + return 0; +} + + + +/* A delproc for a List Node containing a List *. */ +static void +ls_delproc (Node *p) +{ + dellist ((List **)&p->data); +} + + + +/* + * Add this directory to the list of data to be printed for a directory and + * decide whether to tell the recursion processor whether to continue + * recursing or not. + */ +static Dtype +ls_direntproc (void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries) +{ + Dtype retval; + Node *p; + + /* Due to the way we called start_recursion() from ls_proc() with a single + * argument at a time, we can assume that if we don't yet have a parent + * directory in DIRS then this directory should be processed. + */ + + if (strcmp (dir, ".")) + { + /* Search for our parent directory. */ + char *parent; + parent = xmalloc (strlen (update_dir) - strlen (dir) + 1); + strncpy (parent, update_dir, strlen (update_dir) - strlen (dir)); + parent[strlen (update_dir) - strlen (dir)] = '\0'; + strip_trailing_slashes (parent); + p = findnode (callerdat, parent); + } + else + p = NULL; + + if (p) + { + /* Push this dir onto our parent directory's listing. */ + size_t length; + Node *n = getnode(); + + if (entries_format) + n->data = asnprintf (NULL, &length, "D/%s////\n", dir); + else if (long_format) + { + struct long_format_data *out = + xmalloc (sizeof (struct long_format_data)); + out->header = xstrdup ("d--- "); + out->time = gmformat_time_t (unix_time_stamp (repos)); + out->footer = asnprintf (NULL, &length, "%12s%s%s", "", + show_dead_revs ? " " : "", dir); + n->data = out; + n->delproc = long_format_data_delproc; + } + else + n->data = asnprintf (NULL, &length, "%s\n", dir); + + addnode (p->data, n); + } + + if (!p || recurse) + { + /* Create a new list for this directory. */ + p = getnode (); + p->key = xstrdup (strcmp (update_dir, ".") ? update_dir : ""); + p->data = getlist (); + p->delproc = ls_delproc; + addnode (callerdat, p); + + /* Create a local directory and mark it as needing deletion. This is + * the behavior the recursion processor relies upon, a la update & + * checkout. + */ + if (!isdir (dir)) + { + int nonbranch; + if (show_tag == NULL && show_date == NULL) + { + ParseTag (&show_tag, &show_date, &nonbranch); + set_tag = true; + } + + if (!created_dir) + created_dir = xstrdup (update_dir); + + make_directory (dir); + Create_Admin (dir, update_dir, repos, show_tag, show_date, + nonbranch, 0, 0); + Subdir_Register (entries, NULL, dir); + } + + /* Tell do_recursion to keep going. */ + retval = R_PROCESS; + } + else + retval = R_SKIP_ALL; + + return retval; +} + + + +/* Clean up tags, dates, and dirs if we created this directory. + */ +static int +ls_dirleaveproc (void *callerdat, const char *dir, int err, + const char *update_dir, List *entries) +{ + if (created_dir && !strcmp (created_dir, update_dir)) + { + if (set_tag) + { + if (show_tag) free (show_tag); + if (show_date) free (show_date); + show_tag = show_date = NULL; + set_tag = false; + } + + (void)CVS_CHDIR (".."); + if (unlink_file_dir (dir)) + error (0, errno, "Failed to remove directory `%s'", + created_dir); + Subdir_Deregister (entries, NULL, dir); + + free (created_dir); + created_dir = NULL; + } + return err; +} + + + +static int +ls_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile, + int shorten, int local, char *mname, char *msg) +{ + char *repository; + int err = 0; + int which; + char *where; + int i; + + if (is_rls) + { + char *myargv[2]; + + if (!quiet) + error (0, 0, "Listing module: `%s'", + strcmp (mname, "") ? mname : "."); + + repository = xmalloc (strlen (current_parsed_root->directory) + + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + + 2); + (void)sprintf (repository, "%s/%s", current_parsed_root->directory, + argv[0]); + where = xmalloc (strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + + 1); + (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; + + /* If the portion of the module is a path, put the dir part on + * repos. + */ + if ((cp = strrchr (mfile, '/')) != NULL) + { + *cp = '\0'; + (void)strcat (repository, "/"); + (void)strcat (repository, mfile); + (void)strcat (where, "/"); + (void)strcat (where, mfile); + mfile = cp + 1; + } + + /* take care of the rest */ + path = asnprintf (NULL, NULL, "%s/%s", repository, mfile); + if (isdir (path)) + { + /* directory means repository gets the dir tacked on */ + (void)strcpy (repository, path); + (void)strcat (where, "/"); + (void)strcat (where, mfile); + } + else + { + myargv[1] = mfile; + argc = 2; + argv = myargv; + } + free (path); + } + + /* cd to the starting repository */ + if (CVS_CHDIR (repository) < 0) + { + error (0, errno, "cannot chdir to %s", repository); + free (repository); + free (where); + return 1; + } + + which = W_REPOS; + } + else /* !is_rls */ + { + repository = NULL; + where = NULL; + which = W_LOCAL | W_REPOS; + } + + if (show_tag || show_date || show_dead_revs) + which |= W_ATTIC; + + if (show_tag != NULL && !tag_validated) + { + tag_check_valid (show_tag, argc - 1, argv + 1, local, 0, repository); + tag_validated = true; + } + + /* Loop on argc so that we are guaranteed that any directory passed to + * ls_direntproc should be processed if its parent is not yet in DIRS. + */ + if (argc == 1) + { + List *dirs = getlist (); + err = start_recursion (ls_fileproc, NULL, ls_direntproc, + ls_dirleaveproc, dirs, 0, NULL, local, which, 0, + CVS_LOCK_READ, where, 1, repository); + walklist (dirs, ls_print_dir, NULL); + dellist (&dirs); + } + else + { + for (i = 1; i < argc; i++) + { + List *dirs = getlist (); + err = start_recursion (ls_fileproc, NULL, ls_direntproc, + NULL, dirs, 1, argv + i, local, which, 0, + CVS_LOCK_READ, where, 1, repository); + walklist (dirs, ls_print_dir, NULL); + dellist (&dirs); + } + } + + if (!(which & W_LOCAL)) free (repository); + if (where) free (where); + + return err; +} diff --git a/contrib/cvs-1.12.9/src/main.c b/contrib/cvs-1.12.9/src/main.c new file mode 100644 index 0000000000..75c220ba24 --- /dev/null +++ b/contrib/cvs-1.12.9/src/main.c @@ -0,0 +1,1307 @@ +/* + * 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 source + * distribution. + * + * This is the main C driver for the CVS system. + * + * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing + * the shell-script CVS system that this is based on. + * + */ + +#include "cvs.h" + +#ifdef HAVE_WINSOCK_H +# include +#elif !HAVE_GETHOSTNAME +extern int gethostname (char *, int); +#endif + +const char *program_name; +const char *program_path; +const char *cvs_cmd_name; + +/* I'd dynamically allocate this, but it seems like gethostname + requires a fixed size array. If I'm remembering the RFCs right, + 256 should be enough. */ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +char hostname[MAXHOSTNAMELEN]; + +int use_editor = 1; +int use_cvsrc = 1; +int cvswrite = !CVSREAD_DFLT; +int really_quiet = 0; +int quiet = 0; +int trace = 0; +int noexec = 0; +int readonlyfs = 0; +int logoff = 0; + +/* Set if we should be writing CVSADM directories at top level. At + least for now we'll make the default be off (the CVS 1.9, not CVS + 1.9.2, behavior). */ +int top_level_admin = 0; +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS +int UseNewInfoFmtStrings = 0; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + +mode_t cvsumask = UMASK_DFLT; + +char *CurDir; + +/* + * Defaults, for the environment variables that are not set + */ +char *Tmpdir = TMPDIR_DFLT; +char *Editor = EDITOR_DFLT; + + +/* When our working directory contains subdirectories with different + values in CVS/Root files, we maintain a list of them. */ +List *root_directories = NULL; + +/* We step through the above values. This variable is set to reflect + * the currently active value. + * + * Now static. FIXME - this variable should be removable (well, localizable) + * with a little more work. + */ +static char *current_root = NULL; + + +static const struct cmd +{ + char *fullname; /* Full name of the function (e.g. "commit") */ + + /* Synonyms for the command, nick1 and nick2. We supply them + mostly for two reasons: (1) CVS has always supported them, and + we need to maintain compatibility, (2) if there is a need for a + version which is shorter than the fullname, for ease in typing. + Synonyms have the disadvantage that people will see "new" and + then have to think about it, or look it up, to realize that is + the operation they know as "add". Also, this means that one + cannot create a command "cvs new" with a different meaning. So + new synonyms are probably best used sparingly, and where used + should be abbreviations of the fullname (preferably consisting + of the first 2 or 3 or so letters). + + One thing that some systems do is to recognize any unique + abbreviation, for example "annotat" "annota", etc., for + "annotate". The problem with this is that scripts and user + habits will expect a certain abbreviation to be unique, and in + a future release of CVS it may not be. So it is better to + accept only an explicit list of abbreviations and plan on + supporting them in the future as well as now. */ + + char *nick1; + char *nick2; + + int (*func) (); /* Function takes (argc, argv) arguments. */ + unsigned long attr; /* Attributes. */ +} cmds[] = + +{ + { "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "annotate", "ann", NULL, annotate, CVS_CMD_USES_WORK_DIR }, + { "checkout", "co", "get", checkout, 0 }, + { "commit", "ci", "com", commit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "diff", "di", "dif", diff, CVS_CMD_USES_WORK_DIR }, + { "edit", NULL, NULL, edit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "editors", NULL, NULL, editors, CVS_CMD_USES_WORK_DIR }, + { "export", "exp", "ex", checkout, CVS_CMD_USES_WORK_DIR }, + { "history", "hi", "his", history, CVS_CMD_USES_WORK_DIR }, + { "import", "im", "imp", import, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR | CVS_CMD_IGNORE_ADMROOT}, + { "init", NULL, NULL, init, CVS_CMD_MODIFIES_REPOSITORY }, +#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT) + { "kserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */ +#endif + { "log", "lo", NULL, cvslog, CVS_CMD_USES_WORK_DIR }, +#ifdef AUTH_CLIENT_SUPPORT + { "login", "logon", "lgn", login, 0 }, + { "logout", NULL, NULL, logout, 0 }, +#endif /* AUTH_CLIENT_SUPPORT */ + { "ls", "dir", "list", ls, 0 }, +#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT) + { "pserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */ +#endif + { "rannotate","rann", "ra", annotate, 0 }, + { "rdiff", "patch", "pa", patch, 0 }, + { "release", "re", "rel", release, 0 }, + { "remove", "rm", "delete", cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "rlog", "rl", NULL, cvslog, 0 }, + { "rls", "rdir", "rlist", ls, 0 }, + { "rtag", "rt", "rfreeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY }, +#ifdef SERVER_SUPPORT + { "server", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, +#endif + { "status", "st", "stat", cvsstatus, CVS_CMD_USES_WORK_DIR }, + { "tag", "ta", "freeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "unedit", NULL, NULL, unedit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "update", "up", "upd", update, CVS_CMD_USES_WORK_DIR }, + { "version", "ve", "ver", version, 0 }, + { "watch", NULL, NULL, watch, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "watchers", NULL, NULL, watchers, CVS_CMD_USES_WORK_DIR }, + { NULL, NULL, NULL, NULL, 0 }, +}; + +static const char *const usg[] = +{ + /* CVS usage messages never have followed the GNU convention of + putting metavariables in uppercase. I don't know whether that + is a good convention or not, but if it changes it would have to + change in all the usage messages. For now, they consistently + use lowercase, as far as I know. Punctuation is pretty funky, + though. Sometimes they use none, as here. Sometimes they use + single quotes (not the TeX-ish `' stuff), as in --help-options. + Sometimes they use double quotes, as in cvs -H add. + + Most (not all) of the usage messages seem to have periods at + the end of each line. I haven't tried to duplicate this style + in --help as it is a rather different format from the rest. */ + + "Usage: %s [cvs-options] command [command-options-and-arguments]\n", + " where cvs-options are -q, -n, etc.\n", + " (specify --help-options for a list of options)\n", + " where command is add, admin, etc.\n", + " (specify --help-commands for a list of commands\n", + " or --help-synonyms for a list of command synonyms)\n", + " where command-options-and-arguments depend on the specific command\n", + " (specify -H followed by a command name for command-specific help)\n", + " Specify --help to receive this message\n", + "\n", + + /* Some people think that a bug-reporting address should go here. IMHO, + the web sites are better because anything else is very likely to go + obsolete in the years between a release and when someone might be + reading this help. Besides, we could never adequately discuss + bug reporting in a concise enough way to put in a help message. */ + + /* I was going to put this at the top, but usage() wants the %s to + be in the first line. */ + "The Concurrent Versions System (CVS) is a tool for version control.\n", + /* I really don't think I want to try to define "version control" + in one line. I'm not sure one can get more concise than the + paragraph in ../cvs.spec without assuming the reader knows what + version control means. */ + + "For CVS updates and additional information, see\n", + " the CVS home page at http://www.cvshome.org/ or\n", + " Pascal Molli's CVS site at http://www.loria.fr/~molli/cvs-index.html\n", + NULL, +}; + +static const char *const cmd_usage[] = +{ + "CVS commands are:\n", + " add Add a new file/directory to the repository\n", + " admin Administration front end for rcs\n", + " annotate Show last revision where each line was modified\n", + " checkout Checkout sources for editing\n", + " commit Check files into the repository\n", + " diff Show differences between revisions\n", + " edit Get ready to edit a watched file\n", + " editors See who is editing a watched file\n", + " export Export sources from CVS, similar to checkout\n", + " history Show repository access history\n", + " import Import sources into CVS, using vendor branches\n", + " init Create a CVS repository if it doesn't exist\n", +#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT) + " kserver Kerberos server mode\n", +#endif + " log Print out history information for files\n", +#ifdef AUTH_CLIENT_SUPPORT + " login Prompt for password for authenticating server\n", + " logout Removes entry in .cvspass for remote repository\n", +#endif /* AUTH_CLIENT_SUPPORT */ + " ls List files available from CVS\n", +#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT) + " pserver Password server mode\n", +#endif + " rannotate Show last revision where each line of module was modified\n", + " rdiff Create 'patch' format diffs between releases\n", + " release Indicate that a Module is no longer in use\n", + " remove Remove an entry from the repository\n", + " rlog Print out history information for a module\n", + " rls List files in a module\n", + " rtag Add a symbolic tag to a module\n", +#ifdef SERVER_SUPPORT + " server Server mode\n", +#endif + " status Display status information on checked out files\n", + " tag Add a symbolic tag to checked out version of files\n", + " unedit Undo an edit command\n", + " update Bring work tree in sync with repository\n", + " version Show current CVS version(s)\n", + " watch Set watches\n", + " watchers See who is watching a file\n", + "(Specify the --help option for a list of other help options)\n", + NULL, +}; + +static const char *const opt_usage[] = +{ + /* Omit -b because it is just for compatibility. */ + "CVS global options (specified before the command name) are:\n", + " -H Displays usage information for command.\n", + " -Q Cause CVS to be really quiet.\n", + " -q Cause CVS to be somewhat quiet.\n", + " -r Make checked-out files read-only.\n", + " -w Make checked-out files read-write (default).\n", + " -n Do not execute anything that will change the disk.\n", + " -t Show trace of program execution (repeat for more\n", + " verbosity) -- try with -n.\n", + " -R Assume repository is read-only, such as CDROM\n", + " -v CVS version and copyright.\n", + " -T tmpdir Use 'tmpdir' for temporary files.\n", + " -e editor Use 'editor' for editing log information.\n", + " -d CVS_root Overrides $CVSROOT as the root of the CVS tree.\n", + " -f Do not use the ~/.cvsrc file.\n", +#ifdef CLIENT_SUPPORT + " -z # Use compression level '#' for net traffic.\n", +#ifdef ENCRYPTION + " -x Encrypt all net traffic.\n", +#endif + " -a Authenticate all net traffic.\n", +#endif + " -s VAR=VAL Set CVS user variable.\n", + "(Specify the --help option for a list of other help options)\n", + NULL +}; + + +static int +set_root_directory (Node *p, void *ignored) +{ + if (current_root == NULL && p->data == NULL) + { + current_root = p->key; + return 1; + } + return 0; +} + + +static const char * const* +cmd_synonyms (void) +{ + char ** synonyms; + char ** line; + const struct cmd *c = &cmds[0]; + /* Three more for title, "specify --help" line, and NULL. */ + int numcmds = 3; + + while (c->fullname != NULL) + { + numcmds++; + c++; + } + + synonyms = (char **) xmalloc(numcmds * sizeof(char *)); + line = synonyms; + *line++ = "CVS command synonyms are:\n"; + for (c = &cmds[0]; c->fullname != NULL; c++) + { + if (c->nick1 || c->nick2) + { + *line = xmalloc (strlen (c->fullname) + + (c->nick1 != NULL ? strlen (c->nick1) : 0) + + (c->nick2 != NULL ? strlen (c->nick2) : 0) + + 40); + sprintf(*line, " %-12s %s %s\n", c->fullname, + c->nick1 ? c->nick1 : "", + c->nick2 ? c->nick2 : ""); + line++; + } + } + *line++ = "(Specify the --help option for a list of other help options)\n"; + *line = NULL; + + return (const char * const*) synonyms; /* will never be freed */ +} + + +unsigned long int +lookup_command_attribute (char *cmd_name) +{ + const struct cmd *cm; + + for (cm = cmds; cm->fullname; cm++) + { + if (strcmp (cmd_name, cm->fullname) == 0) + break; + } + if (!cm->fullname) + error (1, 0, "unknown command: %s", cmd_name); + return cm->attr; +} + + + +/* + * Exit with an error code and an informative message about the signal + * received. This function, by virtue of causing an actual call to exit(), + * causes all the atexit() handlers to be called. + * + * INPUTS + * sig The signal recieved. + * + * ERRORS + * The cleanup routines registered via atexit() and the error function + * itself can potentially change the exit status. They shouldn't do this + * unless they encounter problems doing their own jobs. + * + * RETURNS + * Nothing. This function will always exit. It should exit with an exit + * status of 1, but might not, as noted in the ERRORS section above. + */ +static RETSIGTYPE +main_cleanup (int sig) +{ +#ifndef DONT_USE_SIGNALS + const char *name; + char temp[10]; + + switch (sig) + { +#ifdef SIGABRT + case SIGABRT: + name = "abort"; + break; +#endif +#ifdef SIGHUP + case SIGHUP: + name = "hangup"; + break; +#endif +#ifdef SIGINT + case SIGINT: + name = "interrupt"; + break; +#endif +#ifdef SIGQUIT + case SIGQUIT: + name = "quit"; + break; +#endif +#ifdef SIGPIPE + case SIGPIPE: + name = "broken pipe"; + break; +#endif +#ifdef SIGTERM + case SIGTERM: + name = "termination"; + break; +#endif + default: + /* This case should never be reached, because we list above all + the signals for which we actually establish a signal handler. */ + sprintf (temp, "%d", sig); + name = temp; + break; + } + + error (1, 0, "received %s signal", name); +#endif /* !DONT_USE_SIGNALS */ +} + + + +int +main (int argc, char **argv) +{ + char *CVSroot = CVSROOT_DFLT; + char *cp, *end; + const struct cmd *cm; + int c, err = 0; + int tmpdir_update_env, cvs_update_env; + int free_CVSroot = 0; + int free_Editor = 0; + int free_Tmpdir = 0; + + int help = 0; /* Has the user asked for help? This + lets us support the `cvs -H cmd' + convention to give help for cmd. */ + static const char short_options[] = "+QqrwtnRvb:T:e:d:Hfz:s:xa"; + static struct option long_options[] = + { + {"help", 0, NULL, 'H'}, + {"version", 0, NULL, 'v'}, + {"help-commands", 0, NULL, 1}, + {"help-synonyms", 0, NULL, 2}, + {"help-options", 0, NULL, 4}, + {"allow-root", required_argument, NULL, 3}, + {0, 0, 0, 0} + }; + /* `getopt_long' stores the option index here, but right now we + don't use it. */ + int option_index = 0; + +#ifdef SYSTEM_INITIALIZE + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_INITIALIZE (&argc, &argv); +#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. */ + cleanup_register (SYSTEM_CLEANUP); +#endif + +#ifdef HAVE_TZSET + /* On systems that have tzset (which is almost all the ones I know + of), it's a good idea to call it. */ + tzset (); +#endif + + /* + * Just save the last component of the path for error messages + */ + program_path = xstrdup (argv[0]); +#ifdef ARGV0_NOT_PROGRAM_NAME + /* On some systems, e.g. VMS, argv[0] is not the name of the command + which the user types to invoke the program. */ + program_name = "cvs"; +#else + program_name = last_component (argv[0]); +#endif + + /* + * Query the environment variables up-front, so that + * they can be overridden by command line arguments + */ + cvs_update_env = 0; + tmpdir_update_env = *Tmpdir; /* TMPDIR_DFLT must be set */ + if ((cp = getenv (TMPDIR_ENV)) != NULL) + { + Tmpdir = cp; + tmpdir_update_env = 0; /* it's already there */ + } + if ((cp = getenv (EDITOR1_ENV)) != NULL) + Editor = cp; + else if ((cp = getenv (EDITOR2_ENV)) != NULL) + Editor = cp; + else if ((cp = getenv (EDITOR3_ENV)) != NULL) + Editor = cp; + if ((cp = getenv (CVSROOT_ENV)) != NULL) + { + CVSroot = cp; + cvs_update_env = 0; /* it's already there */ + } + if (getenv (CVSREAD_ENV) != NULL) + cvswrite = 0; + if (getenv (CVSREADONLYFS_ENV) != NULL) { + readonlyfs = 1; + logoff = 1; + } + + /* Set this to 0 to force getopt initialization. getopt() sets + this to 1 internally. */ + optind = 0; + + /* We have to parse the options twice because else there is no + chance to avoid reading the global options from ".cvsrc". Set + opterr to 0 for avoiding error messages about invalid options. + */ + opterr = 0; + + while ((c = getopt_long + (argc, argv, short_options, long_options, &option_index)) + != EOF) + { + if (c == 'f') + use_cvsrc = 0; + } + + /* + * Scan cvsrc file for global options. + */ + if (use_cvsrc) + read_cvsrc (&argc, &argv, "cvs"); + + optind = 0; + opterr = 1; + + while ((c = getopt_long + (argc, argv, short_options, long_options, &option_index)) + != EOF) + { + switch (c) + { + case 1: + /* --help-commands */ + usage (cmd_usage); + break; + case 2: + /* --help-synonyms */ + usage (cmd_synonyms()); + break; + case 4: + /* --help-options */ + usage (opt_usage); + break; + case 3: + /* --allow-root */ + root_allow_add (optarg); + break; + case 'Q': + really_quiet = 1; + /* FALL THROUGH */ + case 'q': + quiet = 1; + break; + case 'r': + cvswrite = 0; + break; + case 'w': + cvswrite = 1; + break; + case 't': + trace++; + break; + case 'R': + readonlyfs = -1; + logoff = 1; + break; + case 'n': + noexec = 1; + logoff = 1; + break; + case 'v': + fputs ("\n", stdout); + version (0, NULL); + fputs ( +"\n" +"Copyright (c) 1989-2004 Brian Berliner, david d `zoo' zuhn,\n" +" Jeff Polk, and other authors\n" +"\n" +"CVS may be copied only under the terms of the GNU General Public License,\n" +"a copy of which can be found with the CVS distribution kit.\n" +"\n" +"Specify the --help option for further information about CVS\n", stdout); + + exit (0); + break; + case 'b': + /* This option used to specify the directory for RCS + executables. But since we don't run them any more, + this is a noop. Silently ignore it so that .cvsrc + and scripts and inetd.conf and such can work with + either new or old CVS. */ + break; + case 'T': + if (free_Tmpdir) free (Tmpdir); + Tmpdir = xstrdup (optarg); + free_Tmpdir = 1; + tmpdir_update_env = 1; /* need to update environment */ + break; + case 'e': + if (free_Editor) free (Editor); + Editor = xstrdup (optarg); + free_Editor = 1; + break; + case 'd': + if (CVSroot_cmdline != NULL) + free (CVSroot_cmdline); + CVSroot_cmdline = xstrdup (optarg); + if (free_CVSroot) + { + free (CVSroot); + free_CVSroot = 0; + } + CVSroot = CVSroot_cmdline; + cvs_update_env = 1; /* need to update environment */ + break; + case 'H': + help = 1; + break; + case 'f': + use_cvsrc = 0; /* unnecessary, since we've done it above */ + break; + case 'z': +#ifdef CLIENT_SUPPORT + gzip_level = strtol (optarg, &end, 10); + if (*end != '\0' || gzip_level < 0 || gzip_level > 9) + error (1, 0, + "gzip compression level must be between 0 and 9"); +#endif /* CLIENT_SUPPORT */ + /* If no CLIENT_SUPPORT, we just silently ignore the gzip + * level, so that users can have it in their .cvsrc and not + * cause any trouble. + * + * We still parse the argument to -z for correctness since + * one user complained of being bitten by a run of + * `cvs -z -n up' which read -n as the argument to -z without + * complaining. */ + break; + case 's': + variable_set (optarg); + break; + case 'x': +#ifdef CLIENT_SUPPORT + cvsencrypt = 1; +#endif /* CLIENT_SUPPORT */ + /* If no CLIENT_SUPPORT, ignore -x, so that users can + have it in their .cvsrc and not cause any trouble. + If no ENCRYPTION, we still accept -x, but issue an + error if we are being run as a client. */ + break; + case 'a': +#ifdef CLIENT_SUPPORT + cvsauthenticate = 1; +#endif + /* If no CLIENT_SUPPORT, ignore -a, so that users can + have it in their .cvsrc and not cause any trouble. + We will issue an error later if stream + authentication is not supported. */ + break; + case '?': + default: + usage (usg); + } + } + + argc -= optind; + argv += optind; + if (argc < 1) + usage (usg); + + if (readonlyfs && !really_quiet) { + error (0, 0, + "WARNING: Read-only repository access mode selected via `cvs -R'.\n\ +Using this option to access a repository which some users write to may\n\ +cause intermittent sandbox corruption."); + } + + /* Look up the command name. */ + + cvs_cmd_name = argv[0]; + for (cm = cmds; cm->fullname; cm++) + { + if (cm->nick1 && !strcmp (cvs_cmd_name, cm->nick1)) + break; + if (cm->nick2 && !strcmp (cvs_cmd_name, cm->nick2)) + break; + if (!strcmp (cvs_cmd_name, cm->fullname)) + break; + } + + if (!cm->fullname) + { + fprintf (stderr, "Unknown command: `%s'\n\n", cvs_cmd_name); + usage (cmd_usage); + } + else + cvs_cmd_name = cm->fullname; /* Global pointer for later use */ + + if (help) + { + argc = -1; /* some functions only check for this */ + err = (*(cm->func)) (argc, argv); + } + else + { + /* The user didn't ask for help, so go ahead and authenticate, + set up CVSROOT, and the rest of it. */ + + short int lock_cleanup_setup = 0; + + /* The UMASK environment variable isn't handled with the + others above, since we don't want to signal errors if the + user has asked for help. This won't work if somebody adds + a command-line flag to set the umask, since we'll have to + parse it before we get here. */ + + if ((cp = getenv (CVSUMASK_ENV)) != NULL) + { + /* FIXME: Should be accepting symbolic as well as numeric mask. */ + cvsumask = strtol (cp, &end, 8) & 0777; + if (*end != '\0') + error (1, errno, "invalid umask value in %s (%s)", + CVSUMASK_ENV, cp); + } + +#ifdef SERVER_SUPPORT + +# ifdef HAVE_KERBEROS + /* If we are invoked with a single argument "kserver", then we are + running as Kerberos server as root. Do the authentication as + the very first thing, to minimize the amount of time we are + running as root. */ + if (strcmp (cvs_cmd_name, "kserver") == 0) + { + kserver_authenticate_connection (); + + /* Pretend we were invoked as a plain server. */ + cvs_cmd_name = "server"; + } +# endif /* HAVE_KERBEROS */ + + +# if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI) + if (strcmp (cvs_cmd_name, "pserver") == 0) + { + /* The reason that --allow-root is not a command option + is mainly the comment in server() about how argc,argv + might be from .cvsrc. I'm not sure about that, and + I'm not sure it is only true of command options, but + it seems easier to make it a global option. */ + + /* Gets username and password from client, authenticates, then + switches to run as that user and sends an ACK back to the + client. */ + pserver_authenticate_connection (); + + /* Pretend we were invoked as a plain server. */ + cvs_cmd_name = "server"; + } +# endif /* AUTH_SERVER_SUPPORT || HAVE_GSSAPI */ + + server_active = strcmp (cvs_cmd_name, "server") == 0; + +#endif /* SERVER_SUPPORT */ + + +#ifdef SERVER_SUPPORT + if (server_active) + { + /* This is only used for writing into the history file. For + remote connections, it might be nice to have hostname + and/or remote path, on the other hand I'm not sure whether + it is worth the trouble. */ + CurDir = xstrdup (""); + cleanup_register (server_cleanup); + } + else +#endif + { + CurDir = xgetwd (); + if (CurDir == NULL) + error (1, errno, "cannot get working directory"); + } + + if (Tmpdir == NULL || Tmpdir[0] == '\0') + Tmpdir = "/tmp"; + +#ifdef HAVE_PUTENV + if (tmpdir_update_env) + { + char *env; + env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1); + (void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir); + (void) putenv (env); + /* do not free env, as putenv has control of it */ + } + { + char *env; + /* XXX pid < 10^32 */ + env = xmalloc (strlen (CVS_PID_ENV) + 1 + 32 + 1); + (void) sprintf (env, "%s=%ld", CVS_PID_ENV, (long) getpid ()); + (void) putenv (env); + /* do not free env, as putenv has control of it */ + } +#endif + + /* make sure we clean up on error */ + signals_register (main_cleanup); + + gethostname(hostname, sizeof (hostname)); + +#ifdef KLUDGE_FOR_WNT_TESTSUITE + /* Probably the need for this will go away at some point once + we call fflush enough places (e.g. fflush (stdout) in + cvs_outerr). */ + (void) setvbuf (stdout, (char *) NULL, _IONBF, 0); + (void) setvbuf (stderr, (char *) NULL, _IONBF, 0); +#endif /* KLUDGE_FOR_WNT_TESTSUITE */ + + if (use_cvsrc) + read_cvsrc (&argc, &argv, cvs_cmd_name); + +#ifdef SERVER_SUPPORT + /* Fiddling with CVSROOT doesn't make sense if we're running + in server mode, since the client will send the repository + directory after the connection is made. */ + + if (!server_active) +#endif + { + char *CVSADM_Root; + + /* See if we are able to find a 'better' value for CVSroot + in the CVSADM_ROOT directory. */ + + CVSADM_Root = NULL; + + /* "cvs import" shouldn't check CVS/Root; in general it + ignores CVS directories and CVS/Root is likely to + specify a different repository than the one we are + importing to. */ + + if (!(cm->attr & CVS_CMD_IGNORE_ADMROOT) + + /* -d overrides CVS/Root, so don't give an error if the + latter points to a nonexistent repository. */ + && CVSroot_cmdline == NULL) + { + CVSADM_Root = Name_Root((char *) NULL, (char *) NULL); + } + + if (CVSADM_Root != NULL) + { + if (CVSroot == NULL || !cvs_update_env) + { + CVSroot = CVSADM_Root; + cvs_update_env = 1; /* need to update environment */ + } + } + + /* Now we've reconciled CVSROOT from the command line, the + CVS/Root file, and the environment variable. Do the + last sanity checks on the variable. */ + + if (! CVSroot) + { + error (0, 0, + "No CVSROOT specified! Please use the `-d' option"); + error (1, 0, + "or set the %s environment variable.", CVSROOT_ENV); + } + + if (! *CVSroot) + { + error (0, 0, + "CVSROOT is set but empty! Make sure that the"); + error (0, 0, + "specification of CVSROOT is valid, either via the"); + error (0, 0, + "`-d' option, the %s environment variable, or the", + CVSROOT_ENV); + error (1, 0, + "CVS/Root file (if any)."); + } + } + + /* Here begins the big loop over unique cvsroot values. We + need to call do_recursion once for each unique value found + in CVS/Root. Prime the list with the current value. */ + + /* Create the list. */ + assert (root_directories == NULL); + root_directories = getlist (); + + /* Prime it. */ + if (CVSroot != NULL) + { + Node *n; + n = getnode (); + n->type = NT_UNKNOWN; + n->key = xstrdup (CVSroot); + n->data = NULL; + + if (addnode (root_directories, n)) + error (1, 0, "cannot add initial CVSROOT %s", n->key); + } + + assert (current_root == NULL); + + /* If we're running the server, we want to execute this main + loop once and only once (we won't be serving multiple roots + from this connection, so there's no need to do it more than + once). To get out of the loop, we perform a "break" at the + end of things. */ + + while ( +#ifdef SERVER_SUPPORT + server_active || +#endif + walklist (root_directories, set_root_directory, NULL) + ) + { +#ifdef SERVER_SUPPORT + /* Fiddling with CVSROOT doesn't make sense if we're running + in server mode, since the client will send the repository + directory after the connection is made. */ + + if (!server_active) +#endif + { + /* Now we're 100% sure that we have a valid CVSROOT + variable. Parse it to see if we're supposed to do + remote accesses or use a special access method. */ + + if (current_parsed_root != NULL) + free_cvsroot_t (current_parsed_root); + if ((current_parsed_root = parse_cvsroot (current_root)) == NULL) + error (1, 0, "Bad CVSROOT: `%s'.", current_root); + + TRACE ( TRACE_FUNCTION, + "main loop with CVSROOT=%s", + current_root ? current_root : "(null)"); + + /* + * Check to see if the repository exists. + */ +#ifdef CLIENT_SUPPORT + if (!current_parsed_root->isremote) +#endif /* CLIENT_SUPPORT */ + { + char *path; + int save_errno; + + path = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + 2); + (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM); + if (!isaccessible (path, R_OK | X_OK)) + { + save_errno = errno; + /* If this is "cvs init", the root need not exist yet. */ + if (strcmp (cvs_cmd_name, "init") != 0) + { + error (1, save_errno, "%s", path); + } + } + free (path); + } + +#ifdef HAVE_PUTENV + /* Update the CVSROOT environment variable if necessary. */ + /* FIXME (njc): should we always set this with the CVSROOT from the command line? */ + if (cvs_update_env) + { + static char *prev; + char *env; + env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot) + + 1 + 1); + (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot); + (void) putenv (env); + /* do not free env yet, as putenv has control of it */ + /* but do free the previous value, if any */ + if (prev != NULL) + free (prev); + prev = env; + } +#endif + } + + /* Parse the CVSROOT/config file, but only for local. For the + server, we parse it after we know $CVSROOT. For the + client, it doesn't get parsed at all, obviously. The + presence of the parse_config call here is not mean to + predetermine whether CVSROOT/config overrides things from + read_cvsrc and other such places or vice versa. That sort + of thing probably needs more thought. */ + if (1 +#ifdef SERVER_SUPPORT + && !server_active +#endif +#ifdef CLIENT_SUPPORT + && !current_parsed_root->isremote +#endif + ) + { + /* If there was an error parsing the config file, parse_config + already printed an error. We keep going. Why? Because + if we didn't, then there would be no way to check in a new + CVSROOT/config file to fix the broken one! */ + parse_config (current_parsed_root->directory); + } + +#ifdef CLIENT_SUPPORT + /* Need to check for current_parsed_root != NULL here since + * we could still be in server mode before the server function + * gets called below and sets the root + */ + if (current_parsed_root != NULL && current_parsed_root->isremote) + { + /* Create a new list for directory names that we've + sent to the server. */ + if (dirs_sent_to_server != NULL) + dellist (&dirs_sent_to_server); + dirs_sent_to_server = getlist (); + } +#endif + + if ( +#ifdef SERVER_SUPPORT + /* Don't worry about lock_cleanup_setup when the server is + * active since we can only go through this loop once in that + * case anyhow. + */ + server_active || +#endif +#ifdef CLIENT_SUPPORT + !current_parsed_root->isremote && +#endif + !lock_cleanup_setup) + { + /* Set up to clean up any locks we might create on exit. */ + cleanup_register (Lock_Cleanup); + lock_cleanup_setup = 1; + } + + /* Call our worker function. */ + err = (*(cm->func)) (argc, argv); + + /* Mark this root directory as done. When the server is + active, current_root will be NULL -- don't try and + remove it from the list. */ + + if (current_root != NULL) + { + Node *n = findnode (root_directories, current_root); + assert (n != NULL); + n->data = (void *) 1; + current_root = NULL; + } + +#if 0 + /* This will not work yet, since it tries to free (void *) 1. */ + dellist (&root_directories); +#endif + +#ifdef SERVER_SUPPORT + if (server_active) + break; +#endif + } /* end of loop for cvsroot values */ + + } /* end of stuff that gets done if the user DOESN'T ask for help */ + + if (free_CVSroot) + free (CVSroot); + root_allow_free (); + + /* This is exit rather than return because apparently that keeps + some tools which check for memory leaks happier. */ + exit (err ? EXIT_FAILURE : 0); + /* Keep picky/stupid compilers (e.g. Visual C++ 5.0) happy. */ + return 0; +} + + + +char * +Make_Date (char *rawdate) +{ + time_t unixtime = get_date (rawdate, NULL); + if (unixtime == (time_t)-1) + error (1, 0, "Can't parse date/time: `%s'", rawdate); + return date_from_time_t (unixtime); +} + + + +/* Convert a time_t to an RCS format date. This is mainly for the + use of "cvs history", because the CVSROOT/history file contains + time_t format dates; most parts of CVS will want to avoid using + time_t's directly, and instead use RCS_datecmp, Make_Date, &c. + Assuming that the time_t is in GMT (as it generally should be), + then the result will be in GMT too. + + Returns a newly malloc'd string. */ + +char * +date_from_time_t (time_t unixtime) +{ + struct tm *ftm; + char date[MAXDATELEN]; + char *ret; + + ftm = gmtime (&unixtime); + if (ftm == NULL) + /* This is a system, like VMS, where the system clock is in local + time. Hopefully using localtime here matches the "zero timezone" + hack I added to get_date (get_date of course being the relevant + issue for Make_Date, and for history.c too I think). */ + ftm = localtime (&unixtime); + + (void) sprintf (date, DATEFORM, + ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900), + ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour, + ftm->tm_min, ftm->tm_sec); + ret = xstrdup (date); + return ret; +} + + + +/* Convert a date to RFC822/1123 format. This is used in contexts like + dates to send in the protocol; it should not vary based on locale or + other such conventions for users. We should have another routine which + does that kind of thing. + + The SOURCE date is in our internal RCS format. DEST should point to + storage managed by the caller, at least MAXDATELEN characters. */ +void +date_to_internet (char *dest, const char *source) +{ + struct tm date; + + date_to_tm (&date, source); + tm_to_internet (dest, &date); +} + + + +void +date_to_tm (struct tm *dest, const char *source) +{ + if (sscanf (source, SDATEFORM, + &dest->tm_year, &dest->tm_mon, &dest->tm_mday, + &dest->tm_hour, &dest->tm_min, &dest->tm_sec) + != 6) + /* Is there a better way to handle errors here? I made this + non-fatal in case we are called from the code which can't + deal with fatal errors. */ + error (0, 0, "internal error: bad date %s", source); + + if (dest->tm_year > 100) + dest->tm_year -= 1900; + + dest->tm_mon -= 1; +} + + + +/* Convert a date to RFC822/1123 format. This is used in contexts like + dates to send in the protocol; it should not vary based on locale or + other such conventions for users. We should have another routine which + does that kind of thing. + + The SOURCE date is a pointer to a struct tm. DEST should point to + storage managed by the caller, at least MAXDATELEN characters. */ +void +tm_to_internet (char *dest, const struct tm *source) +{ + /* Just to reiterate, these strings are from RFC822 and do not vary + according to locale. */ + static const char *const month_names[] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", source->tm_mday, + source->tm_mon < 0 || source->tm_mon > 11 + ? "???" : month_names[source->tm_mon], + source->tm_year + 1900, source->tm_hour, source->tm_min, + source->tm_sec); +} + + + +/* + * Format a date for the current locale. + * + * INPUT + * UNIXTIME The UNIX seconds since the epoch. + * + * RETURNS + * If my_strftime() encounters an error, this function can return NULL. + * + * Otherwise, returns a date string in ISO8601 format, e.g.: + * + * 2004-04-29 13:24:22 -0700 + * + * It is the responsibility of the caller to return of this string. + */ +static char * +format_time_t (time_t unixtime) +{ + static char buf[sizeof ("yyyy-mm-dd HH:MM:SS -HHMM")]; + /* Convert to a time in the local time zone. */ + struct tm ltm = *(gmtime (&unixtime)); + + if (my_strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S %z", <m) == 0) + return NULL; + + return xstrdup (buf); +} + + + +/* Like format_time_t(), but return time in UTC. + */ +char * +gmformat_time_t (time_t unixtime) +{ + static char buf[sizeof ("yyyy-mm-dd HH:MM:SS -HHMM")]; + /* Convert to a time in the local time zone. */ + struct tm ltm = *(gmtime (&unixtime)); + + if (my_strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S %z", <m) == 0) + return NULL; + + return xstrdup (buf); +} + + + +/* Format a date in the local timezone using format_time_t() given a date from + * an arbitrary timezone in a string. + * + * INPUT + * DATESTR A string that looks like anything get_date() can parse, e.g.: + * + * 2004-04-29 20:24:22 + * + * ERRORS + * As get_date() & format_time_t(). Prints a warning if either provide + * error return values. See RETURNS. + * + * RETURNS + * A freshly allocated string that is a copy of the input string if either + * get_date() or format_time_t() encounter an error and as format_time_t() + * otherwise. + */ +char * +format_date_alloc (char *datestr) +{ + time_t unixtime; + char *buf; + + TRACE (TRACE_FUNCTION, "format_date (%s)", datestr); + + /* Convert the date string to seconds since the epoch. */ + unixtime = get_date (datestr, NULL); + if (unixtime == (time_t)-1) + { + error (0, 0, "Can't parse date/time: `%s'.", datestr); + goto as_is; + } + + /* Get the time into a string. */ + if ((buf = format_time_t (unixtime)) == NULL) + { + error (0, 0, "Unable to reformat date `%s'.", datestr); + goto as_is; + } + + return buf; + + as_is: + return xstrdup (datestr); +} + + + +void +usage (register const char *const *cpp) +{ + (void) fprintf (stderr, *cpp++, program_name, cvs_cmd_name); + for (; *cpp; cpp++) + (void) fprintf (stderr, *cpp); + exit (EXIT_FAILURE); +} + +/* vim:tabstop=8:shiftwidth=4 + */ diff --git a/contrib/cvs-1.12.9/src/mkmodules.c b/contrib/cvs-1.12.9/src/mkmodules.c new file mode 100644 index 0000000000..485c44fe59 --- /dev/null +++ b/contrib/cvs-1.12.9/src/mkmodules.c @@ -0,0 +1,1015 @@ +/* + * 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. */ + +#include "cvs.h" +#include "getline.h" +#include "history.h" +#include "savecwd.h" + +#ifndef DBLKSIZ +#define DBLKSIZ 4096 /* since GNU ndbm doesn't define it */ +#endif + +static int checkout_file (char *file, char *temp); +static char *make_tempfile (void); +static void rename_rcsfile (char *temp, char *real); + +#ifndef MY_NDBM +static void rename_dbmfile (char *temp); +static void write_dbmfile (char *temp); +#endif /* !MY_NDBM */ + +/* Structure which describes an administrative file. */ +struct admin_file { + /* Name of the file, within the CVSROOT directory. */ + char *filename; + + /* This is a one line description of what the file is for. It is not + currently used, although one wonders whether it should be, somehow. + If NULL, then don't process this file in mkmodules (FIXME?: a bit of + a kludge; probably should replace this with a flags field). */ + char *errormsg; + + /* Contents which the file should have in a new repository. To avoid + problems with brain-dead compilers which choke on long string constants, + this is a pointer to an array of char * terminated by NULL--each of + the strings is concatenated. + + If this field is NULL, the file is not created in a new + repository, but it can be added with "cvs add" (just as if one + had created the repository with a version of CVS which didn't + know about the file) and the checked-out copy will be updated + without having to add it to checkoutlist. */ + const char * const *contents; +}; + +static const char *const loginfo_contents[] = { + "# The \"loginfo\" file controls where \"cvs commit\" log information\n", + "# is sent. The first entry on a line is a regular expression which must match\n", + "# the directory that the change is being made to, relative to the\n", + "# $CVSROOT. If a match is found, then the remainder of the line is a filter\n", + "# program that should expect log information on its standard input.\n", + "#\n", + "# If the repository name does not match any of the regular expressions in this\n", + "# file, the \"DEFAULT\" line is used, if it is specified.\n", + "#\n", + "# If the name ALL appears as a regular expression it is always used\n", + "# in addition to the first matching regex or DEFAULT.\n", + "#\n", + "# If any format strings are present in the filter, they will be replaced as follows:\n", + "# %p = path relative to repository\n", + "# %r = repository (path portion of $CVSROOT)\n", + "# %{sVv} = attribute list = file name, old version number (pre-checkin),\n", + "# new version number (post-checkin). When either old or new revision is\n", + "# unknown, doesn't exist, or isn't applicable, the string \"NONE\" will be\n", + "# placed on the command line instead.\n", + "#\n", + "# Note that %{sVv} is a list operator and not all elements are necessary. Thus %{sv} is\n", + "# a legal format string, but will only be replaced with file name and new revision.\n", + "# it also generates multiple arguments for each file being operated upon. i.e. if two\n", + "# files, file1 & file2, are being commited from 1.1 to version 1.1.2.1 and from 1.1.2.2\n", + "# to 1.1.2.3, respectively, %{sVv} will generate the following six arguments in this\n", + "# order: file1, 1.1, 1.1.2.1, file2, 1.1.2.2, 1.1.2.3.\n", + "#\n", + "# For example:\n", + "#DEFAULT (echo \"\"; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", + "# or\n", + "#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", + NULL +}; + +static const char *const rcsinfo_contents[] = { + "# The \"rcsinfo\" file is used to control templates with which the editor\n", + "# is invoked on commit and import.\n", + "#\n", + "# The first entry on a line is a regular expression which is tested\n", + "# against the directory that the change is being made to, relative to the\n", + "# $CVSROOT. For the first match that is found, then the remainder of the\n", + "# line is the name of the file that contains the template.\n", + "#\n", + "# If the repository name does not match any of the regular expressions in this\n", + "# file, the \"DEFAULT\" line is used, if it is specified.\n", + "#\n", + "# If the name \"ALL\" appears as a regular expression it is always used\n", + "# in addition to the first matching regex or \"DEFAULT\".\n", + NULL +}; + + + +static const char *const verifymsg_contents[] = { + "# The \"verifymsg\" file is used to allow verification of logging\n", + "# information. It works best when a template (as specified in the\n", + "# rcsinfo file) is provided for the logging procedure. Given a\n", + "# template with locations for, a bug-id number, a list of people who\n", + "# reviewed the code before it can be checked in, and an external\n", + "# process to catalog the differences that were code reviewed, the\n", + "# following test can be applied to the code:\n", + "#\n", + "# Making sure that the entered bug-id number is correct.\n", + "# Validating that the code that was reviewed is indeed the code being\n", + "# checked in (using the bug-id number or a seperate review\n", + "# number to identify this particular code set.).\n", + "#\n", + "# If any of the above test failed, then the commit would be aborted.\n", + "#\n", + "# Format strings present in the filter will be replaced as follows:\n", + "# %p = path relative to repository\n", + "# %r = repository (path portion of $CVSROOT)\n", + "# %l = name of log file to be verified.\n", + "#\n", + "# If no format strings are present in the filter, a default \" %l\" will\n", + "# be appended to the filter, but this usage is deprecated.\n", + "#\n", + "# Actions such as mailing a copy of the report to each reviewer are\n", + "# better handled by an entry in the loginfo file.\n", + "#\n", + "# One thing that should be noted is the the ALL keyword is not\n", + "# supported. There can be only one entry that matches a given\n", + "# repository.\n", + NULL +}; + +static const char *const commitinfo_contents[] = { + "# The \"commitinfo\" file is used to control pre-commit checks.\n", + "# The filter on the right is invoked with the repository and a list \n", + "# of files to check. A non-zero exit of the filter program will \n", + "# cause the commit to be aborted.\n", + "#\n", + "# The first entry on a line is a regular expression which is tested\n", + "# against the directory that the change is being committed to, relative\n", + "# to the $CVSROOT. For the first match that is found, then the remainder\n", + "# of the line is the name of the filter to run.\n", + "#\n", + "# Format strings present in the filter will be replaced as follows:\n", + "# %p = path relative to repository\n", + "# %r = repository (path portion of $CVSROOT)\n", + "# %{s} = file name, file name, ...\n", + "#\n", + "# If no format strings are present in the filter string, a default of\n", + "# \" %r %s\" will be appended to the filter string, but this usage is\n", + "# deprecated.\n", + "#\n", + "# If the repository name does not match any of the regular expressions in this\n", + "# file, the \"DEFAULT\" line is used, if it is specified.\n", + "#\n", + "# If the name \"ALL\" appears as a regular expression it is always used\n", + "# in addition to the first matching regex or \"DEFAULT\".\n", + NULL +}; + +static const char *const taginfo_contents[] = { + "# The \"taginfo\" file is used to control pre-tag checks.\n", + "# The filter on the right is invoked with the following arguments if no format strings are present:\n", + "#\n", + "# $1 -- tagname\n", + "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for tag -d\n", + "# $3 -- tagtype \"?\" on delete, \"T\" for branch, \"N\" for static\n", + "# $4 -- repository\n", + "# $5-> file revision [file revision ...]\n", + "#\n", + "# If any format strings are present in the filter, they will be replaced as follows:\n", + "# %b = branch mode = \"?\" (delete ops - unknown) | \"T\" (branch) | \"N\" (not branch)\n", + "# %o = operation = \"add\" | \"mov\" | \"del\"\n", + "# %p = path relative to repository\n", + "# %r = repository (path portion of $CVSROOT)\n", + "# %t = tagname\n", + "# %{sVv} = attribute list = file name, old version tag will be deleted from,\n", + "# new version tag will be added to (or deleted from, but this feature is\n", + "# deprecated. When either old or new revision is unknown, doesn't exist,\n", + "# or isn't applicable, the string \"NONE\" will be placed on the command\n", + "# line.\n", + "#\n", + "# Note that %{sVv} is a list operator and not all elements are necessary. Thus %{sV} is\n", + "# a legal format string, but will only be replaced with file name and old revision.\n", + "# it also generates multiple arguments for each file being operated upon. i.e. if two\n", + "# files, file1 & file2, are having a tag moved from version 1.1 to versoin 1.1.2.9, %{sVv}\n", + "# will generate the following six arguments in this order: file1, 1.1, 1.1.2.9, file2, 1.1,\n", + "# 1.1.2.9.\n", + "#\n", + "# A non-zero exit of the filter program will cause the tag to be aborted.\n", + "#\n", + "# The first entry on a line is a regular expression which is tested\n", + "# against the directory that the change is being committed to, relative\n", + "# to the $CVSROOT. For the first match that is found, then the remainder\n", + "# of the line is the name of the filter to run.\n", + "#\n", + "# If the repository name does not match any of the regular expressions in this\n", + "# file, the \"DEFAULT\" line is used, if it is specified.\n", + "#\n", + "# If the name \"ALL\" appears as a regular expression it is always used\n", + "# in addition to the first matching regex or \"DEFAULT\".\n", + NULL +}; + +static const char *const checkoutlist_contents[] = { + "# The \"checkoutlist\" file is used to support additional version controlled\n", + "# administrative files in $CVSROOT/CVSROOT, such as template files.\n", + "#\n", + "# The first entry on a line is a filename which will be checked out from\n", + "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n", + "# The remainder of the line is an error message to use if the file cannot\n", + "# be checked out.\n", + "#\n", + "# File format:\n", + "#\n", + "# [][]\n", + "#\n", + "# comment lines begin with '#'\n", + NULL +}; + +static const char *const cvswrappers_contents[] = { + "# This file affects handling of files based on their names.\n", + "#\n", +#if 0 /* see comments in wrap_add in wrapper.c */ + "# The -t/-f options allow one to treat directories of files\n", + "# as a single file, or to transform a file in other ways on\n", + "# its way in and out of CVS.\n", + "#\n", +#endif + "# The -m option specifies whether CVS attempts to merge files.\n", + "#\n", + "# The -k option specifies keyword expansion (e.g. -kb for binary).\n", + "#\n", + "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n", + "#\n", + "# wildcard [option value][option value]...\n", + "#\n", + "# where option is one of\n", + "# -f from cvs filter value: path to filter\n", + "# -t to cvs filter value: path to filter\n", + "# -m update methodology value: MERGE or COPY\n", + "# -k expansion mode value: b, o, kkv, &c\n", + "#\n", + "# and value is a single-quote delimited value.\n", + "# For example:\n", + "#*.gif -k 'b'\n", + NULL +}; + +static const char *const notify_contents[] = { + "# The \"notify\" file controls where notifications from watches set by\n", + "# \"cvs watch add\" or \"cvs edit\" are sent. The first entry on a line is\n", + "# a regular expression which is tested against the directory that the\n", + "# change is being made to, relative to the $CVSROOT. If it matches,\n", + "# then the remainder of the line is a filter program that should contain\n", + "# one occurrence of %s for the user to notify, and information on its\n", + "# standard input.\n", + "#\n", + "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n", + "#\n", + "# format strings are replaceed as follows:\n", + "# %p = path relative to repository\n", + "# %r = repository (path portion of $CVSROOT)\n", + "# %s = user to notify\n", + "#\n", + "# For example:\n", + "#ALL (echo Committed to %r/%p; cat) |mail %s -s \"CVS notification\"\n", + NULL +}; + +static const char *const modules_contents[] = { + "# Three different line formats are valid:\n", + "# key -a aliases...\n", + "# key [options] directory\n", + "# key [options] directory files...\n", + "#\n", + "# Where \"options\" are composed of:\n", + "# -i prog Run \"prog\" on \"cvs commit\" from top-level of module.\n", + "# -o prog Run \"prog\" on \"cvs checkout\" of module.\n", + "# -e prog Run \"prog\" on \"cvs export\" of module.\n", + "# -t prog Run \"prog\" on \"cvs rtag\" of module.\n", + "# -u prog Run \"prog\" on \"cvs update\" of module.\n", + "# -d dir Place module in directory \"dir\" instead of module name.\n", + "# -l Top-level directory only -- do not recurse.\n", + "#\n", + "# NOTE: If you change any of the \"Run\" options above, you'll have to\n", + "# release and re-checkout any working directories of these modules.\n", + "#\n", + "# And \"directory\" is a path to a directory relative to $CVSROOT.\n", + "#\n", + "# The \"-a\" option specifies an alias. An alias is interpreted as if\n", + "# everything on the right of the \"-a\" had been typed on the command line.\n", + "#\n", + "# You can encode a module within a module by using the special '&'\n", + "# character to interpose another module into the current module. This\n", + "# can be useful for creating a module that consists of many directories\n", + "# spread out over the entire source repository.\n", + NULL +}; + +static const char *const config_contents[] = { + "# Set this to \"no\" if pserver shouldn't check system users/passwords\n", + "#SystemAuth=no\n", + "\n", + "# Put CVS lock files in this directory rather than directly in the repository.\n", + "#LockDir=/var/lock/cvs\n", + "\n", +#ifdef PRESERVE_PERMISSIONS_SUPPORT + "# Set `PreservePermissions' to `yes' to save file status information\n", + "# in the repository.\n", + "#PreservePermissions=no\n", + "\n", +#endif + "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n", + "# level of the new working directory when using the `cvs checkout'\n", + "# command.\n", + "#TopLevelAdmin=no\n", + "\n", + "# Set `LogHistory' to `all' or `" ALL_HISTORY_REC_TYPES "' to log all transactions to the\n", + "# history file, or a subset as needed (ie `TMAR' logs all write operations)\n", + "#LogHistory=" ALL_HISTORY_REC_TYPES "\n", + "\n", + "# Set `RereadLogAfterVerify' to `always' (the default) to allow the verifymsg\n", + "# script to change the log message. Set it to `stat' to force CVS to verify\n", + "# that the file has changed before reading it (this can take up to an extra\n", + "# second per directory being committed, so it is not recommended for large\n", + "# repositories. Set it to `never' (the previous CVS behavior) to prevent\n", + "# verifymsg scripts from changing the log message.\n", + "#RereadLogAfterVerify=always\n", + "\n", + "# Set `UserAdminOptions' to the list of `cvs admin' commands (options)\n", + "# that users not in the `cvsadmin' group are allowed to run. This\n", + "# defaults to `k', or only allowing the changing of the default\n", + "# keyword expansion mode for files for users not in the `cvsadmin' group.\n", + "# This value is ignored if the `cvsadmin' group does not exist.\n", + "#\n", + "# The following string would enable all `cvs admin' commands for all\n", + "# users:\n", + "#UserAdminOptions=aAbceIklLmnNostuU\n", +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + "\n", + "# Set `UseNewInfoFmtStrings' to `no' if you must support a legacy system by\n", + "# enabling the deprecated old style info file command line format strings.\n", + "# Be warned that these strings could be disabled in any new version of CVS.\n", + "UseNewInfoFmtStrings=yes\n", +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + NULL +}; + +static const struct admin_file filelist[] = { + {CVSROOTADM_LOGINFO, + "no logging of 'cvs commit' messages is done without a %s file", + &loginfo_contents[0]}, + {CVSROOTADM_RCSINFO, + "a %s file can be used to configure 'cvs commit' templates", + rcsinfo_contents}, + {CVSROOTADM_VERIFYMSG, + "a %s file can be used to validate log messages", + verifymsg_contents}, + {CVSROOTADM_COMMITINFO, + "a %s file can be used to configure 'cvs commit' checking", + commitinfo_contents}, + {CVSROOTADM_TAGINFO, + "a %s file can be used to configure 'cvs tag' checking", + taginfo_contents}, + {CVSROOTADM_IGNORE, + "a %s file can be used to specify files to ignore", + NULL}, + {CVSROOTADM_CHECKOUTLIST, + "a %s file can specify extra CVSROOT files to auto-checkout", + checkoutlist_contents}, + {CVSROOTADM_WRAPPER, + "a %s file can be used to specify files to treat as wrappers", + cvswrappers_contents}, + {CVSROOTADM_NOTIFY, + "a %s file can be used to specify where notifications go", + notify_contents}, + {CVSROOTADM_MODULES, + /* modules is special-cased in mkmodules. */ + NULL, + modules_contents}, + {CVSROOTADM_READERS, + "a %s file specifies read-only users", + NULL}, + {CVSROOTADM_WRITERS, + "a %s file specifies read/write users", + NULL}, + + /* Some have suggested listing CVSROOTADM_PASSWD here too. This + would mean that CVS commands which operate on the + CVSROOTADM_PASSWD file would transmit hashed passwords over the + net. This might seem to be no big deal, as pserver normally + transmits cleartext passwords, but the difference is that + CVSROOTADM_PASSWD contains *all* passwords, not just the ones + currently being used. For example, it could be too easy to + accidentally give someone readonly access to CVSROOTADM_PASSWD + (e.g. via anonymous CVS or cvsweb), and then if there are any + guessable passwords for read/write access (usually there will be) + they get read/write access. + + Another worry is the implications of storing old passwords--if + someone used a password in the past they might be using it + elsewhere, using a similar password, etc, and so saving old + passwords, even hashed, is probably not a good idea. */ + + {CVSROOTADM_CONFIG, + "a %s file configures various behaviors", + config_contents}, + {NULL, NULL, NULL} +}; + +/* Rebuild the checked out administrative files in directory DIR. */ +int +mkmodules (char *dir) +{ + struct saved_cwd cwd; + char *temp; + char *cp, *last, *fname; +#ifdef MY_NDBM + DBM *db; +#endif + FILE *fp; + char *line = NULL; + size_t line_allocated = 0; + const struct admin_file *fileptr; + + if (noexec) + return 0; + + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + + if ( CVS_CHDIR (dir) < 0) + error (1, errno, "cannot chdir to %s", dir); + + /* + * First, do the work necessary to update the "modules" database. + */ + temp = make_tempfile (); + switch (checkout_file (CVSROOTADM_MODULES, temp)) + { + + case 0: /* everything ok */ +#ifdef MY_NDBM + /* open it, to generate any duplicate errors */ + if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL) + dbm_close (db); +#else + write_dbmfile (temp); + rename_dbmfile (temp); +#endif + rename_rcsfile (temp, CVSROOTADM_MODULES); + break; + + default: + error (0, 0, + "'cvs checkout' is less functional without a %s file", + CVSROOTADM_MODULES); + break; + } /* switch on checkout_file() */ + + if (unlink_file (temp) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", temp); + free (temp); + + /* Checkout the files that need it in CVSROOT dir */ + for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) { + if (fileptr->errormsg == NULL) + continue; + temp = make_tempfile (); + if (checkout_file (fileptr->filename, temp) == 0) + rename_rcsfile (temp, fileptr->filename); +#if 0 + /* + * If there was some problem other than the file not existing, + * checkout_file already printed a real error message. If the + * file does not exist, it is harmless--it probably just means + * that the repository was created with an old version of CVS + * which didn't have so many files in CVSROOT. + */ + else if (fileptr->errormsg) + error (0, 0, fileptr->errormsg, fileptr->filename); +#endif + if (unlink_file (temp) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", temp); + free (temp); + } + + fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r"); + if (fp) + { + /* + * File format: + * [][] + * + * comment lines begin with '#' + */ + while (getline (&line, &line_allocated, fp) >= 0) + { + /* skip lines starting with # */ + if (line[0] == '#') + continue; + + if ((last = strrchr (line, '\n')) != NULL) + *last = '\0'; /* strip the newline */ + + /* Skip leading white space. */ + for (fname = line; + *fname && isspace ((unsigned char) *fname); + fname++) + ; + + /* Find end of filename. */ + for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++) + ; + *cp = '\0'; + + temp = make_tempfile (); + if (checkout_file (fname, temp) == 0) + { + rename_rcsfile (temp, fname); + } + else + { + /* Skip leading white space before the error message. */ + for (cp++; + cp < last && *cp && isspace ((unsigned char) *cp); + cp++) + ; + if (cp < last && *cp) + error (0, 0, "%s", cp); + } + if (unlink_file (temp) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", temp); + free (temp); + } + if (line) + free (line); + if (ferror (fp)) + error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST); + } + else + { + /* Error from CVS_FOPEN. */ + if (!existence_error (errno)) + error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST); + } + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + + return (0); +} + +/* + * Yeah, I know, there are NFS race conditions here. + */ +static char * +make_tempfile (void) +{ + static int seed = 0; + int fd; + char *temp; + + if (seed == 0) + seed = getpid (); + temp = xmalloc (sizeof (BAKPREFIX) + 40); + while (1) + { + (void) sprintf (temp, "%s%d", BAKPREFIX, seed++); + if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1) + break; + if (errno != EEXIST) + error (1, errno, "cannot create temporary file %s", temp); + } + if (close(fd) < 0) + error(1, errno, "cannot close temporary file %s", temp); + return temp; +} + +/* Get a file. If the file does not exist, return 1 silently. If + there is an error, print a message and return 1 (FIXME: probably + not a very clean convention). On success, return 0. */ + +static int +checkout_file (char *file, char *temp) +{ + char *rcs; + RCSNode *rcsnode; + int retcode = 0; + + if (noexec) + return 0; + + rcs = xmalloc (strlen (file) + 5); + strcpy (rcs, file); + strcat (rcs, RCSEXT); + if (!isfile (rcs)) + { + free (rcs); + return (1); + } + rcsnode = RCS_parsercsfile (rcs); + retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp, + (RCSCHECKOUTPROC) NULL, (void *) NULL); + if (retcode != 0) + { + /* Probably not necessary (?); RCS_checkout already printed a + message. */ + error (0, 0, "failed to check out %s file", + file); + } + freercsnode (&rcsnode); + free (rcs); + return (retcode); +} + +#ifndef MY_NDBM + +static void +write_dbmfile( char *temp ) +{ + char line[DBLKSIZ], value[DBLKSIZ]; + FILE *fp; + DBM *db; + char *cp, *vp; + datum key, val; + int len, cont, err = 0; + + fp = open_file (temp, "r"); + if ((db = dbm_open (temp, O_RDWR | O_CREAT | O_TRUNC, 0666)) == NULL) + error (1, errno, "cannot open dbm file %s for creation", temp); + for (cont = 0; fgets (line, sizeof (line), fp) != NULL;) + { + if ((cp = strrchr (line, '\n')) != NULL) + *cp = '\0'; /* strip the newline */ + + /* + * Add the line to the value, at the end if this is a continuation + * line; otherwise at the beginning, but only after any trailing + * backslash is removed. + */ + vp = value; + if (cont) + vp += strlen (value); + + /* + * See if the line we read is a continuation line, and strip the + * backslash if so. + */ + len = strlen (line); + if (len > 0) + cp = &line[len - 1]; + else + cp = line; + if (*cp == '\\') + { + cont = 1; + *cp = '\0'; + } + else + { + cont = 0; + } + (void) strcpy (vp, line); + if (value[0] == '#') + continue; /* comment line */ + vp = value; + while (*vp && isspace ((unsigned char) *vp)) + vp++; + if (*vp == '\0') + continue; /* empty line */ + + /* + * If this was not a continuation line, add the entry to the database + */ + if (!cont) + { + key.dptr = vp; + while (*vp && !isspace ((unsigned char) *vp)) + vp++; + key.dsize = vp - key.dptr; + *vp++ = '\0'; /* NULL terminate the key */ + while (*vp && isspace ((unsigned char) *vp)) + vp++; /* skip whitespace to value */ + if (*vp == '\0') + { + error (0, 0, "warning: NULL value for key `%s'", key.dptr); + continue; + } + val.dptr = vp; + val.dsize = strlen (vp); + if (dbm_store (db, key, val, DBM_INSERT) == 1) + { + error (0, 0, "duplicate key found for `%s'", key.dptr); + err++; + } + } + } + dbm_close (db); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", temp); + if (err) + { + /* I think that the size of the buffer needed here is + just determined by sizeof (CVSROOTADM_MODULES), the + filenames created by make_tempfile, and other things that won't + overflow. */ + char dotdir[50], dotpag[50], dotdb[50]; + + (void) sprintf (dotdir, "%s.dir", temp); + (void) sprintf (dotpag, "%s.pag", temp); + (void) sprintf (dotdb, "%s.db", temp); + if (unlink_file (dotdir) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", dotdir); + if (unlink_file (dotpag) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", dotpag); + if (unlink_file (dotdb) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", dotdb); + error (1, 0, "DBM creation failed; correct above errors"); + } +} + +static void +rename_dbmfile( char *temp ) +{ + /* I think that the size of the buffer needed here is + just determined by sizeof (CVSROOTADM_MODULES), the + filenames created by make_tempfile, and other things that won't + overflow. */ + char newdir[50], newpag[50], newdb[50]; + char dotdir[50], dotpag[50], dotdb[50]; + char bakdir[50], bakpag[50], bakdb[50]; + + int dir1_errno = 0, pag1_errno = 0, db1_errno = 0; + int dir2_errno = 0, pag2_errno = 0, db2_errno = 0; + int dir3_errno = 0, pag3_errno = 0, db3_errno = 0; + + (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES); + (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES); + (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES); + (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES); + (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES); + (void) sprintf (bakdb, "%s%s.db", BAKPREFIX, CVSROOTADM_MODULES); + (void) sprintf (newdir, "%s.dir", temp); + (void) sprintf (newpag, "%s.pag", temp); + (void) sprintf (newdb, "%s.db", temp); + + (void) chmod (newdir, 0666); + (void) chmod (newpag, 0666); + (void) chmod (newdb, 0666); + + /* don't mess with me */ + SIG_beginCrSect (); + + /* rm .#modules.dir .#modules.pag */ + if (unlink_file (bakdir) < 0) + dir1_errno = errno; + if (unlink_file (bakpag) < 0) + pag1_errno = errno; + if (unlink_file (bakdb) < 0) + db1_errno = errno; + + /* mv modules.dir .#modules.dir */ + if (CVS_RENAME (dotdir, bakdir) < 0) + dir2_errno = errno; + /* mv modules.pag .#modules.pag */ + if (CVS_RENAME (dotpag, bakpag) < 0) + pag2_errno = errno; + /* mv modules.db .#modules.db */ + if (CVS_RENAME (dotdb, bakdb) < 0) + db2_errno = errno; + + /* mv "temp".dir modules.dir */ + if (CVS_RENAME (newdir, dotdir) < 0) + dir3_errno = errno; + /* mv "temp".pag modules.pag */ + if (CVS_RENAME (newpag, dotpag) < 0) + pag3_errno = errno; + /* mv "temp".db modules.db */ + if (CVS_RENAME (newdb, dotdb) < 0) + db3_errno = errno; + + /* OK -- make my day */ + SIG_endCrSect (); + + /* I didn't want to call error() when we had signals blocked + (unnecessary?), but do it now. */ + if (dir1_errno && !existence_error (dir1_errno)) + error (0, dir1_errno, "cannot remove %s", bakdir); + if (pag1_errno && !existence_error (pag1_errno)) + error (0, pag1_errno, "cannot remove %s", bakpag); + if (db1_errno && !existence_error (db1_errno)) + error (0, db1_errno, "cannot remove %s", bakdb); + + if (dir2_errno && !existence_error (dir2_errno)) + error (0, dir2_errno, "cannot remove %s", bakdir); + if (pag2_errno && !existence_error (pag2_errno)) + error (0, pag2_errno, "cannot remove %s", bakpag); + if (db2_errno && !existence_error (db2_errno)) + error (0, db2_errno, "cannot remove %s", bakdb); + + if (dir3_errno && !existence_error (dir3_errno)) + error (0, dir3_errno, "cannot remove %s", bakdir); + if (pag3_errno && !existence_error (pag3_errno)) + error (0, pag3_errno, "cannot remove %s", bakpag); + if (db3_errno && !existence_error (db3_errno)) + error (0, db3_errno, "cannot remove %s", bakdb); +} + +#endif /* !MY_NDBM */ + +static void +rename_rcsfile (char *temp, char *real) +{ + char *bak; + struct stat statbuf; + char *rcs; + + /* Set "x" bits if set in original. */ + rcs = xmalloc (strlen (real) + sizeof (RCSEXT) + 10); + (void) sprintf (rcs, "%s%s", real, RCSEXT); + statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */ + if (CVS_STAT (rcs, &statbuf) < 0 + && !existence_error (errno)) + error (0, errno, "cannot stat %s", rcs); + free (rcs); + + if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0) + error (0, errno, "warning: cannot chmod %s", temp); + bak = xmalloc (strlen (real) + sizeof (BAKPREFIX) + 10); + (void) sprintf (bak, "%s%s", BAKPREFIX, real); + + /* rm .#loginfo */ + if (unlink_file (bak) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", bak); + + /* mv loginfo .#loginfo */ + if (CVS_RENAME (real, bak) < 0 + && !existence_error (errno)) + error (0, errno, "cannot rename %s to %s", real, bak); + + /* mv "temp" loginfo */ + if (CVS_RENAME (temp, real) < 0 + && !existence_error (errno)) + error (0, errno, "cannot rename %s to %s", temp, real); + + free (bak); +} + +const char *const init_usage[] = { + "Usage: %s %s\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +init (int argc, char **argv) +{ + /* Name of CVSROOT directory. */ + char *adm; + /* Name of this administrative file. */ + char *info; + /* Name of ,v file for this administrative file. */ + char *info_v; + /* Exit status. */ + int err = 0; + + const struct admin_file *fileptr; + + umask (cvsumask); + + if (argc == -1 || argc > 1) + usage (init_usage); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + start_server (); + + ign_setup (); + send_init_command (); + return get_responses_and_close (); + } +#endif /* CLIENT_SUPPORT */ + + /* Note: we do *not* create parent directories as needed like the + old cvsinit.sh script did. Few utilities do that, and a + non-existent parent directory is as likely to be a typo as something + which needs to be created. */ + mkdir_if_needed (current_parsed_root->directory); + + adm = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + 2); + sprintf (adm, "%s/%s", current_parsed_root->directory, CVSROOTADM); + mkdir_if_needed (adm); + + /* This is needed because we pass "fileptr->filename" not "info" + to add_rcs_file below. I think this would be easy to change, + thus nuking the need for CVS_CHDIR here, but I haven't looked + closely (e.g. see wrappers calls within add_rcs_file). */ + if ( CVS_CHDIR (adm) < 0) + error (1, errno, "cannot change to directory %s", adm); + + /* Make Emptydir so it's there if we need it */ + mkdir_if_needed (CVSNULLREPOS); + + /* 80 is long enough for all the administrative file names, plus + "/" and so on. */ + info = xmalloc (strlen (adm) + 80); + info_v = xmalloc (strlen (adm) + 80); + for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr) + { + if (fileptr->contents == NULL) + continue; + strcpy (info, adm); + strcat (info, "/"); + strcat (info, fileptr->filename); + strcpy (info_v, info); + strcat (info_v, RCSEXT); + if (isfile (info_v)) + /* We will check out this file in the mkmodules step. + Nothing else is required. */ + ; + else + { + int retcode; + + if (!isfile (info)) + { + FILE *fp; + const char * const *p; + + fp = open_file (info, "w"); + for (p = fileptr->contents; *p != NULL; ++p) + if (fputs (*p, fp) < 0) + error (1, errno, "cannot write %s", info); + if (fclose (fp) < 0) + error (1, errno, "cannot close %s", info); + } + /* The message used to say " of " and fileptr->filename after + "initial checkin" but I fail to see the point as we know what + file it is from the name. */ + retcode = add_rcs_file ("initial checkin", info_v, + fileptr->filename, "1.1", NULL, + + /* No vendor branch. */ + NULL, NULL, 0, NULL, + + NULL, 0, NULL); + if (retcode != 0) + /* add_rcs_file already printed an error message. */ + err = 1; + } + } + + /* Turn on history logging by default. The user can remove the file + to disable it. */ + strcpy (info, adm); + strcat (info, "/"); + strcat (info, CVSROOTADM_HISTORY); + if (!isfile (info)) + { + FILE *fp; + + fp = open_file (info, "w"); + if (fclose (fp) < 0) + error (1, errno, "cannot close %s", info); + + /* Make the new history file world-writeable, since every CVS + user will need to be able to write to it. We use chmod() + because xchmod() is too shy. */ + chmod (info, 0666); + } + + /* Make an empty val-tags file to prevent problems creating it later. */ + strcpy (info, adm); + strcat (info, "/"); + strcat (info, CVSROOTADM_VALTAGS); + if (!isfile (info)) + { + FILE *fp; + + fp = open_file (info, "w"); + if (fclose (fp) < 0) + error (1, errno, "cannot close %s", info); + + /* Make the new val-tags file world-writeable, since every CVS + user will need to be able to write to it. We use chmod() + because xchmod() is too shy. */ + chmod (info, 0666); + } + + free (info); + free (info_v); + + mkmodules (adm); + + free (adm); + return err; +} diff --git a/contrib/cvs-1.12.9/src/modules.c b/contrib/cvs-1.12.9/src/modules.c new file mode 100644 index 0000000000..dc89b68dfd --- /dev/null +++ b/contrib/cvs-1.12.9/src/modules.c @@ -0,0 +1,1054 @@ +/* + * 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 source + * distribution. + * + * Modules + * + * Functions for accessing the modules file. + * + * The modules file supports basically three formats of lines: + * key [options] directory files... [ -x directory [files] ] ... + * key [options] directory [ -x directory [files] ] ... + * key -a aliases... + * + * The -a option allows an aliasing step in the parsing of the modules + * file. The "aliases" listed on a line following the -a are + * processed one-by-one, as if they were specified as arguments on the + * command line. + */ + +#include "cvs.h" +#include "savecwd.h" + + +/* Defines related to the syntax of the modules file. */ + +/* Options in modules file. Note that it is OK to use GNU getopt features; + we already are arranging to make sure we are using the getopt distributed + with CVS. */ +#define CVSMODULE_OPTS "+ad:lo:e:s:t:" + +/* Special delimiter. */ +#define CVSMODULE_SPEC '&' + +struct sortrec +{ + /* Name of the module, malloc'd. */ + char *modname; + /* If Status variable is set, this is either def_status or the malloc'd + name of the status. If Status is not set, the field is left + uninitialized. */ + char *status; + /* Pointer to a malloc'd array which contains (1) the raw contents + of the options and arguments, excluding comments, (2) a '\0', + and (3) the storage for the "comment" field. */ + char *rest; + char *comment; +}; + +static int sort_order (const void *l, const void *r); +static void save_d (char *k, int ks, char *d, int ds); + + +/* + * Open the modules file, and die if the CVSROOT environment variable + * was not set. If the modules file does not exist, that's fine, and + * a warning message is displayed and a NULL is returned. + */ +DBM * +open_module (void) +{ + char *mfile; + DBM *retval; + + if (current_parsed_root == NULL) + { + error (0, 0, "must set the CVSROOT environment variable"); + error (1, 0, "or specify the '-d' global option"); + } + mfile = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_MODULES) + 3); + (void) sprintf (mfile, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_MODULES); + retval = dbm_open (mfile, O_RDONLY, 0666); + free (mfile); + return retval; +} + +/* + * Close the modules file, if the open succeeded, that is + */ +void +close_module (DBM *db) +{ + if (db != NULL) + dbm_close (db); +} + + + +/* + * This is the recursive function that processes a module name. + * It calls back the passed routine for each directory of a module + * It runs the post checkout or post tag proc from the modules file + */ +int +my_module (DBM *db, char *mname, enum mtype m_type, char *msg, + CALLBACKPROC callback_proc, char *where, int shorten, + int local_specified, int run_module_prog, int build_dirs, + char *extra_arg, List *stack) +{ + char *checkout_prog = NULL; + char *export_prog = NULL; + char *tag_prog = NULL; + struct saved_cwd cwd; + int cwd_saved = 0; + char *line; + int modargc; + int xmodargc; + char **modargv = NULL; + char **xmodargv = NULL; + /* Found entry from modules file, including options and such. */ + char *value = NULL; + char *mwhere = NULL; + char *mfile = NULL; + char *spec_opt = NULL; + 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 = NULL; +#endif + + TRACE (TRACE_FUNCTION, "my_module (%s, %s, %s, %s)", + mname ? mname : "(null)", msg ? msg : "(null)", + where ? where : "NULL", extra_arg ? extra_arg : "NULL"); + + /* Don't process absolute directories. Anything else could be a security + * problem. Before this check was put in place: + * + * $ cvs -d:fork:/cvsroot co /foo + * cvs server: warning: cannot make directory CVS in /: Permission denied + * cvs [server aborted]: cannot make directory /foo: Permission denied + * $ + */ + if (isabsolute (mname)) + error (1, 0, "Absolute module reference invalid: `%s'", mname); + + /* Similarly for directories that attempt to step above the root of the + * repository. + */ + if (pathname_levels (mname) > 0) + error (1, 0, "up-level in module reference (`..') invalid: `%s'.", + mname); + + /* if this is a directory to ignore, add it to that list */ + if (mname[0] == '!' && mname[1] != '\0') + { + ign_dir_add (mname+1); + goto do_module_return; + } + + /* strip extra stuff from the module name */ + strip_trailing_slashes (mname); + + /* + * Look up the module using the following scheme: + * 1) look for mname as a module name + * 2) look for mname as a directory + * 3) look for mname as a file + * 4) take mname up to the first slash and look it up as a module name + * (this is for checking out only part of a module) + */ + + /* look it up as a module name */ + key.dptr = mname; + key.dsize = strlen (key.dptr); + if (db != NULL) + val = dbm_fetch (db, key); + else + val.dptr = NULL; + if (val.dptr != NULL) + { + /* copy and null terminate the value */ + value = xmalloc (val.dsize + 1); + memcpy (value, val.dptr, val.dsize); + value[val.dsize] = '\0'; + + /* If the line ends in a comment, strip it off */ + if ((cp = strchr (value, '#')) != NULL) + *cp = '\0'; + else + cp = value + val.dsize; + + /* Always strip trailing spaces */ + while (cp > value && isspace ((unsigned char) *--cp)) + *cp = '\0'; + + mwhere = xstrdup (mname); + goto found; + } + else + { + char *file; + char *attic_file; + char *acp; + int is_found = 0; + + /* check to see if mname is a directory or file */ + file = xmalloc (strlen (current_parsed_root->directory) + + strlen (mname) + sizeof(RCSEXT) + 2); + (void) sprintf (file, "%s/%s", current_parsed_root->directory, mname); + attic_file = xmalloc (strlen (current_parsed_root->directory) + + strlen (mname) + + sizeof (CVSATTIC) + sizeof (RCSEXT) + 3); + if ((acp = strrchr (mname, '/')) != NULL) + { + *acp = '\0'; + (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory, + mname, CVSATTIC, acp + 1, RCSEXT); + *acp = '/'; + } + else + (void) sprintf (attic_file, "%s/%s/%s%s", + current_parsed_root->directory, + CVSATTIC, mname, RCSEXT); + + if (isdir (file)) + { + modargv = xmalloc (sizeof (*modargv)); + modargv[0] = xstrdup (mname); + modargc = 1; + is_found = 1; + } + else + { + (void) strcat (file, RCSEXT); + if (isfile (file) || isfile (attic_file)) + { + /* if mname was a file, we have to split it into "dir file" */ + if ((cp = strrchr (mname, '/')) != NULL && cp != mname) + { + modargv = xmalloc (2 * sizeof (*modargv)); + modargv[0] = xmalloc (strlen (mname) + 2); + strncpy (modargv[0], mname, cp - mname); + modargv[0][cp - mname] = '\0'; + modargv[1] = xstrdup (cp + 1); + modargc = 2; + } + else + { + /* + * the only '/' at the beginning or no '/' at all + * means the file we are interested in is in CVSROOT + * itself so the directory should be '.' + */ + if (cp == mname) + { + /* drop the leading / if specified */ + modargv = xmalloc (2 * sizeof (*modargv)); + modargv[0] = xstrdup ("."); + modargv[1] = xstrdup (mname + 1); + modargc = 2; + } + else + { + /* otherwise just copy it */ + modargv = xmalloc (2 * sizeof (*modargv)); + modargv[0] = xstrdup ("."); + modargv[1] = xstrdup (mname); + modargc = 2; + } + } + is_found = 1; + } + } + free (attic_file); + free (file); + + if (is_found) + { + assert (value == NULL); + + /* OK, we have now set up modargv with the actual + file/directory we want to work on. We duplicate a + small amount of code here because the vast majority of + the code after the "found" label does not pertain to + the case where we found a file/directory rather than + finding an entry in the modules file. */ + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + cwd_saved = 1; + + err += callback_proc (modargc, modargv, where, mwhere, mfile, + shorten, + local_specified, mname, msg); + + free_names (&modargc, modargv); + + /* cd back to where we started. */ + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + cwd_saved = 0; + + goto do_module_return; + } + } + + /* look up everything to the first / as a module */ + if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL) + { + /* Make the slash the new end of the string temporarily */ + *cp = '\0'; + key.dptr = mname; + key.dsize = strlen (key.dptr); + + /* do the lookup */ + if (db != NULL) + val = dbm_fetch (db, key); + else + val.dptr = NULL; + + /* if we found it, clean up the value and life is good */ + if (val.dptr != NULL) + { + char *cp2; + + /* copy and null terminate the value */ + value = xmalloc (val.dsize + 1); + memcpy (value, val.dptr, val.dsize); + value[val.dsize] = '\0'; + + /* If the line ends in a comment, strip it off */ + if ((cp2 = strchr (value, '#')) != NULL) + *cp2 = '\0'; + else + cp2 = value + val.dsize; + + /* Always strip trailing spaces */ + while (cp2 > value && isspace ((unsigned char) *--cp2)) + *cp2 = '\0'; + + /* mwhere gets just the module name */ + mwhere = xstrdup (mname); + mfile = cp + 1; + + /* put the / back in mname */ + *cp = '/'; + + goto found; + } + + /* put the / back in mname */ + *cp = '/'; + } + + /* if we got here, we couldn't find it using our search, so give up */ + error (0, 0, "cannot find module `%s' - ignored", mname); + err++; + goto do_module_return; + + + /* + * At this point, we found what we were looking for in one + * of the many different forms. + */ + found: + + /* remember where we start */ + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + cwd_saved = 1; + + assert (value != NULL); + + /* search the value for the special delimiter and save for later */ + if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL) + { + *cp = '\0'; /* null out the special char */ + spec_opt = cp + 1; /* save the options for later */ + + /* strip whitespace if necessary */ + while (cp > value && isspace ((unsigned char) *--cp)) + *cp = '\0'; + } + + /* don't do special options only part of a module was specified */ + if (mfile != NULL) + spec_opt = NULL; + + /* + * value now contains one of the following: + * 1) dir + * 2) dir file + * 3) the value from modules without any special args + * [ args ] dir [file] [file] ... + * or -a module [ module ] ... + */ + + /* Put the value on a line with XXX prepended for getopt to eat */ + line = xmalloc (strlen (value) + 5); + strcpy(line, "XXX "); + strcpy(line + 4, value); + + /* turn the line into an argv[] array */ + line2argv (&xmodargc, &xmodargv, line, " \t"); + free (line); + modargc = xmodargc; + modargv = xmodargv; + + /* parse the args */ + optind = 0; + while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1) + { + switch (c) + { + case 'a': + alias = 1; + break; + case 'd': + if (mwhere) + free (mwhere); + mwhere = xstrdup (optarg); + nonalias_opt = 1; + break; + case 'l': + local_specified = 1; + nonalias_opt = 1; + break; + case 'o': + if (checkout_prog) + free (checkout_prog); + checkout_prog = xstrdup (optarg); + nonalias_opt = 1; + break; + case 'e': + if (export_prog) + free (export_prog); + export_prog = xstrdup (optarg); + nonalias_opt = 1; + break; + case 't': + if (tag_prog) + free (tag_prog); + tag_prog = xstrdup (optarg); + nonalias_opt = 1; + break; + case '?': + error (0, 0, + "modules file has invalid option for key %s value %s", + key.dptr, value); + err++; + goto do_module_return; + } + } + modargc -= optind; + modargv += optind; + if (modargc == 0 && spec_opt == NULL) + { + error (0, 0, "modules file missing directory for module %s", mname); + ++err; + goto do_module_return; + } + + if (alias && nonalias_opt) + { + /* The documentation has never said it is valid 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 */ + if (alias) + { + int i; + + for (i = 0; i < modargc; i++) + { + /* + * Recursion check: if an alias module calls itself or a module + * which causes the first to be called again, print an error + * message and stop recursing. + * + * Algorithm: + * + * 1. Check that MNAME isn't in the stack. + * 2. Push MNAME onto the stack. + * 3. Call do_module(). + * 4. Pop MNAME from the stack. + */ + if (stack && findnode (stack, mname)) + error (0, 0, + "module `%s' in modules file contains infinite loop", + mname); + else + { + if (!stack) stack = getlist(); + push_string (stack, mname); + err += my_module (db, modargv[i], m_type, msg, callback_proc, + where, shorten, local_specified, + run_module_prog, build_dirs, extra_arg, + stack); + pop_string (stack); + if (isempty (stack)) dellist (&stack); + } + } + goto do_module_return; + } + + if (mfile != NULL && modargc > 1) + { + error (0, 0, "\ +module `%s' is a request for a file in a module which is not a directory", + mname); + ++err; + goto do_module_return; + } + + /* otherwise, process this module */ + if (modargc > 0) + { + err += callback_proc (modargc, modargv, where, mwhere, mfile, shorten, + local_specified, mname, msg); + } + else + { + /* + * we had nothing but special options, so we must + * make the appropriate directory and cd to it + */ + char *dir; + + if (!build_dirs) + goto do_special; + + dir = where ? where : (mwhere ? mwhere : mname); + /* XXX - think about making null repositories at each dir here + instead of just at the bottom */ + make_directories (dir); + if (CVS_CHDIR (dir) < 0) + { + error (0, errno, "cannot chdir to %s", dir); + spec_opt = NULL; + err++; + goto do_special; + } + if (!isfile (CVSADM)) + { + char *nullrepos; + + nullrepos = emptydir_name (); + + Create_Admin (".", dir, + nullrepos, (char *) NULL, (char *) NULL, 0, 0, 1); + if (!noexec) + { + FILE *fp; + + 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 (dir, nullrepos); +#endif + } + free (nullrepos); + } + } + + /* if there were special include args, process them now */ + + do_special: + + free_names (&xmodargc, xmodargv); + xmodargv = NULL; + + /* blow off special options if -l was specified */ + 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; + + cp = strchr (spec_opt, CVSMODULE_SPEC); + if (cp != NULL) + { + /* save the beginning of the next arg */ + next_opt = cp + 1; + + /* strip whitespace off the end */ + do + *cp = '\0'; + while (cp > spec_opt && isspace ((unsigned char) *--cp)); + } + else + next_opt = NULL; + + /* strip whitespace from front */ + while (isspace ((unsigned char) *spec_opt)) + spec_opt++; + + if (*spec_opt == '\0') + error (0, 0, "Mal-formed %c option for module %s - ignored", + CVSMODULE_SPEC, mname); + else + err += my_module (db, spec_opt, m_type, msg, callback_proc, + (char *) NULL, 0, local_specified, + run_module_prog, build_dirs, extra_arg, + stack); + spec_opt = next_opt; + } + +#ifdef SERVER_SUPPORT + if (server_active && restore_server_dir) + { + free (server_dir); + server_dir = server_dir_to_restore; + } +#endif + + /* cd back to where we started */ + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + cwd_saved = 0; + + /* run checkout or tag prog if appropriate */ + if (err == 0 && run_module_prog) + { + if ((m_type == TAG && tag_prog != NULL) || + (m_type == CHECKOUT && checkout_prog != NULL) || + (m_type == EXPORT && export_prog != NULL)) + { + /* + * If a relative pathname is specified as the checkout, tag + * or export proc, try to tack on the current "where" value. + * if we can't find a matching program, just punt and use + * whatever is specified in the modules file. + */ + char *real_prog = 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; + } + + /* XXX can we determine the line number for this entry??? */ + expanded_path = expand_path (prog, "modules", 0, 0); + if (expanded_path != NULL) + { + run_setup (expanded_path); + run_arg (real_where); + + if (extra_arg) + run_arg (extra_arg); + + if (!quiet) + { + cvs_output (program_name, 0); + cvs_output (" ", 1); + cvs_output (cvs_cmd_name, 0); + cvs_output (": Executing '", 0); + run_print (stdout); + cvs_output ("'\n", 0); + cvs_flushout (); + } + err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + free (expanded_path); + } + free (real_prog); + } + } + + do_module_return: + /* clean up */ + if (xmodargv != NULL) + free_names (&xmodargc, xmodargv); + if (mwhere) + free (mwhere); + if (checkout_prog) + free (checkout_prog); + if (export_prog) + free (export_prog); + if (tag_prog) + free (tag_prog); + if (cwd_saved) + free_cwd (&cwd); + if (value != NULL) + free (value); + + if (xvalue != NULL) + free (xvalue); + return (err); +} + + + +/* External face of do_module so that we can have an internal version which + * accepts a stack argument to track alias recursion. + */ +int +do_module (DBM *db, char *mname, enum mtype m_type, char *msg, + CALLBACKPROC callback_proc, char *where, int shorten, + int local_specified, int run_module_prog, int build_dirs, + char *extra_arg) +{ + return my_module (db, mname, m_type, msg, callback_proc, where, shorten, + local_specified, run_module_prog, build_dirs, extra_arg, + NULL); +} + + + +/* - Read all the records from the modules database into an array. + - Sort the array depending on what format is desired. + - Print the array in the format desired. + + Currently, there are only two "desires": + + 1. Sort by module name and format the whole entry including switches, + files and the comment field: (Including aliases) + + modulename -s switches, one per line, even if + it has many switches. + Directories and files involved, formatted + to cover multiple lines if necessary. + # Comment, also formatted to cover multiple + # lines if necessary. + + 2. Sort by status field string and print: (*not* including aliases) + + modulename STATUS Directories and files involved, formatted + to cover multiple lines if necessary. + # Comment, also formatted to cover multiple + # lines if necessary. +*/ + +static struct sortrec *s_head; + +static int s_max = 0; /* Number of elements allocated */ +static int s_count = 0; /* Number of elements used */ + +static int Status; /* Nonzero if the user is + interested in status + information as well as + module name */ +static char def_status[] = "NONE"; + +/* Sort routine for qsort: + - If we want the "Status" field to be sorted, check it first. + - Then compare the "module name" fields. Since they are unique, we don't + have to look further. +*/ +static int +sort_order (const void *l, const void *r) +{ + int i; + const struct sortrec *left = (const struct sortrec *) l; + const struct sortrec *right = (const struct sortrec *) r; + + if (Status) + { + /* If Sort by status field, compare them. */ + if ((i = strcmp (left->status, right->status)) != 0) + return (i); + } + return (strcmp (left->modname, right->modname)); +} + +static void +save_d (char *k, int ks, char *d, int ds) +{ + char *cp, *cp2; + struct sortrec *s_rec; + + if (Status && *d == '-' && *(d + 1) == 'a') + return; /* We want "cvs co -s" and it is an alias! */ + + if (s_count == s_max) + { + s_max += 64; + s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head)); + } + s_rec = &s_head[s_count]; + s_rec->modname = cp = xmalloc (ks + 1); + (void) strncpy (cp, k, ks); + *(cp + ks) = '\0'; + + s_rec->rest = cp2 = xmalloc (ds + 1); + cp = d; + *(cp + ds) = '\0'; /* Assumes an extra byte at end of static dbm buffer */ + + while (isspace ((unsigned char) *cp)) + cp++; + /* Turn into one ' ' -- makes the rest of this routine simpler */ + while (*cp) + { + if (isspace ((unsigned char) *cp)) + { + *cp2++ = ' '; + while (isspace ((unsigned char) *cp)) + cp++; + } + else + *cp2++ = *cp++; + } + *cp2 = '\0'; + + /* Look for the "-s statusvalue" text */ + if (Status) + { + s_rec->status = def_status; + + for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL; cp = ++cp2) + { + if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ') + { + char *status_start; + + cp2 += 3; + status_start = cp2; + while (*cp2 != ' ' && *cp2 != '\0') + cp2++; + s_rec->status = xmalloc (cp2 - status_start + 1); + strncpy (s_rec->status, status_start, cp2 - status_start); + s_rec->status[cp2 - status_start] = '\0'; + cp = cp2; + break; + } + } + } + else + cp = s_rec->rest; + + /* Find comment field, clean up on all three sides & compress blanks */ + if ((cp2 = cp = strchr (cp, '#')) != NULL) + { + if (*--cp2 == ' ') + *cp2 = '\0'; + if (*++cp == ' ') + cp++; + s_rec->comment = cp; + } + else + s_rec->comment = ""; + + s_count++; +} + +/* Print out the module database as we know it. If STATUS is + non-zero, print out status information for each module. */ + +void +cat_module (int status) +{ + DBM *db; + datum key, val; + int i, c, wid, argc, cols = 80, indent, fill; + int moduleargc; + struct sortrec *s_h; + char *cp, *cp2, **argv; + char **moduleargv; + + Status = status; + + /* Read the whole modules file into allocated records */ + if (!(db = open_module ())) + error (1, 0, "failed to open the modules file"); + + for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db)) + { + val = dbm_fetch (db, key); + if (val.dptr != NULL) + save_d (key.dptr, key.dsize, val.dptr, val.dsize); + } + + close_module (db); + + /* Sort the list as requested */ + qsort ((void *) s_head, s_count, sizeof (struct sortrec), sort_order); + + /* + * Run through the sorted array and format the entries + * indent = space for modulename + space for status field + */ + indent = 12 + (status * 12); + fill = cols - (indent + 2); + for (s_h = s_head, i = 0; i < s_count; i++, s_h++) + { + char *line; + + /* Print module name (and status, if wanted) */ + line = xmalloc (strlen (s_h->modname) + 15); + sprintf (line, "%-12s", s_h->modname); + cvs_output (line, 0); + free (line); + if (status) + { + line = xmalloc (strlen (s_h->status) + 15); + sprintf (line, " %-11s", s_h->status); + cvs_output (line, 0); + free (line); + } + + line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 15); + /* Parse module file entry as command line and print options */ + (void) sprintf (line, "%s %s", s_h->modname, s_h->rest); + line2argv (&moduleargc, &moduleargv, line, " \t"); + free (line); + argc = moduleargc; + argv = moduleargv; + + optind = 0; + wid = 0; + while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1) + { + if (!status) + { + if (c == 'a' || c == 'l') + { + 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) + { + int j; + + cvs_output ("\n", 1); + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); + wid = 0; + } + sprintf (buf, " -%c ", c); + cvs_output (buf, 0); + cvs_output (optarg, 0); + wid += strlen (optarg) + 4; + } + } + } + argc -= optind; + argv += optind; + + /* Format and Print all the files and directories */ + for (; argc--; argv++) + { + if (strlen (*argv) + wid > (unsigned) fill) + { + int j; + + cvs_output ("\n", 1); + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); + wid = 0; + } + cvs_output (" ", 1); + cvs_output (*argv, 0); + wid += strlen (*argv) + 1; + } + cvs_output ("\n", 1); + + /* Format the comment field -- save_d (), compressed spaces */ + for (cp2 = cp = s_h->comment; *cp; cp2 = cp) + { + int j; + + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); + cvs_output (" # ", 0); + if (strlen (cp2) < (unsigned) (fill - 2)) + { + cvs_output (cp2, 0); + cvs_output ("\n", 1); + break; + } + cp += fill - 2; + while (*cp != ' ' && cp > cp2) + cp--; + if (cp == cp2) + { + cvs_output (cp2, 0); + cvs_output ("\n", 1); + break; + } + + *cp++ = '\0'; + cvs_output (cp2, 0); + cvs_output ("\n", 1); + } + + free_names(&moduleargc, moduleargv); + /* FIXME-leak: here is where we would free s_h->modname, s_h->rest, + and if applicable, s_h->status. Not exactly a memory leak, + in the sense that we are about to exit(), but may be worth + noting if we ever do a multithreaded server or something of + the sort. */ + } + /* FIXME-leak: as above, here is where we would free s_head. */ +} diff --git a/contrib/cvs-1.12.9/src/myndbm.c b/contrib/cvs-1.12.9/src/myndbm.c new file mode 100644 index 0000000000..5297140fea --- /dev/null +++ b/contrib/cvs-1.12.9/src/myndbm.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 1992, Brian Berliner + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * A simple ndbm-emulator for CVS. It parses a text file of the format: + * + * key value + * + * at dbm_open time, and loads the entire file into memory. As such, it is + * probably only good for fairly small modules files. Ours is about 30K in + * size, and this code works fine. + */ + +#include "cvs.h" +#include "getline.h" + +#ifdef MY_NDBM +# ifndef O_ACCMODE +# define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) +# endif /* defined O_ACCMODE */ + +static void mydbm_load_file (FILE *, List *, char *); + +/* Returns NULL on error in which case errno has been set to indicate + the error. Can also call error() itself. */ +/* ARGSUSED */ +DBM * +mydbm_open (char *file, int flags, int mode) +{ + FILE *fp; + DBM *db; + + fp = CVS_FOPEN (file, (flags & O_ACCMODE) != O_RDONLY ? + FOPEN_BINARY_READWRITE : FOPEN_BINARY_READ); + if (fp == NULL && !(existence_error (errno) && (flags & O_CREAT))) + return ((DBM *) 0); + + db = (DBM *) xmalloc (sizeof (*db)); + db->dbm_list = getlist (); + db->modified = 0; + db->name = xstrdup (file); + + if (fp != NULL) + { + mydbm_load_file (fp, db->dbm_list, file); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", file); + } + return (db); +} + +static int write_item (Node *, void *); + +static int +write_item (Node *node, void *data) +{ + FILE *fp = (FILE *)data; + fputs (node->key, fp); + fputs (" ", fp); + fputs (node->data, fp); + fputs ("\012", fp); + return 0; +} + +void +mydbm_close (DBM *db) +{ + if (db->modified) + { + FILE *fp; + 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); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", db->name); + } + free (db->name); + dellist (&db->dbm_list); + free ((char *) db); +} + +datum +mydbm_fetch (DBM *db, datum key) +{ + Node *p; + char *s; + datum val; + + /* make sure it's null-terminated */ + s = xmalloc (key.dsize + 1); + (void) strncpy (s, key.dptr, key.dsize); + s[key.dsize] = '\0'; + + p = findnode (db->dbm_list, s); + if (p) + { + val.dptr = p->data; + val.dsize = strlen (p->data); + } + else + { + val.dptr = (char *) NULL; + val.dsize = 0; + } + free (s); + return (val); +} + +datum +mydbm_firstkey (DBM *db) +{ + Node *head, *p; + datum key; + + head = db->dbm_list->list; + p = head->next; + if (p != head) + { + key.dptr = p->key; + key.dsize = strlen (p->key); + } + else + { + key.dptr = (char *) NULL; + key.dsize = 0; + } + db->dbm_next = p->next; + return (key); +} + +datum +mydbm_nextkey (DBM *db) +{ + Node *head, *p; + datum key; + + head = db->dbm_list->list; + p = db->dbm_next; + if (p != head) + { + key.dptr = p->key; + key.dsize = strlen (p->key); + } + else + { + key.dptr = (char *) NULL; + key.dsize = 0; + } + db->dbm_next = p->next; + return (key); +} + +/* Note: only updates the in-memory copy, which is written out at + mydbm_close time. Note: Also differs from DBM in that on duplication, + it gives a warning, rather than either DBM_INSERT or DBM_REPLACE + behavior. */ +int +mydbm_store (DBM *db, datum key, datum value, int flags) +{ + Node *node; + + node = getnode (); + node->type = NDBMNODE; + + node->key = xmalloc (key.dsize + 1); + *node->key = '\0'; + strncat (node->key, key.dptr, key.dsize); + + node->data = xmalloc (value.dsize + 1); + *(char *)node->data = '\0'; + strncat (node->data, value.dptr, value.dsize); + + db->modified = 1; + if (addnode (db->dbm_list, node) == -1) + { + error (0, 0, "attempt to insert duplicate key `%s'", node->key); + freenode (node); + return 0; + } + return 0; +} + +static void +mydbm_load_file (FILE *fp, List *list, char *filename) + + + /* Used in error messages. */ +{ + char *line = NULL; + size_t line_size; + char *value; + size_t value_allocated; + char *cp, *vp; + int cont; + int line_length; + int line_num; + + value_allocated = 1; + value = xmalloc (value_allocated); + + cont = 0; + line_num=0; + while( ( line_length = + getdelim( &line, &line_size, '\012', fp ) ) >= 0 ) + { + line_num++; + if (line_length > 0 && line[line_length - 1] == '\012') + { + /* Strip the newline. */ + --line_length; + line[line_length] = '\0'; + } + if (line_length > 0 && line[line_length - 1] == '\015') + { + /* If the file (e.g. modules) was written on an NT box, it will + contain CRLF at the ends of lines. Strip them (we can't do + this by opening the file in text mode because we might be + running on unix). */ + --line_length; + line[line_length] = '\0'; + } + + /* + * Add the line to the value, at the end if this is a continuation + * line; otherwise at the beginning, but only after any trailing + * backslash is removed. + */ + if (!cont) + value[0] = '\0'; + + /* + * See if the line we read is a continuation line, and strip the + * backslash if so. + */ + if (line_length > 0) + cp = &line[line_length - 1]; + else + cp = line; + if (*cp == '\\') + { + cont = 1; + *cp = '\0'; + --line_length; + } + else + { + cont = 0; + } + expand_string (&value, + &value_allocated, + strlen (value) + line_length + 5); + strcat (value, line); + + if (value[0] == '#') + continue; /* comment line */ + vp = value; + while (*vp && isspace ((unsigned char) *vp)) + vp++; + if (*vp == '\0') + continue; /* empty line */ + + /* + * If this was not a continuation line, add the entry to the database + */ + if (!cont) + { + Node *p = getnode (); + char *kp; + + kp = vp; + while (*vp && !isspace ((unsigned char) *vp)) + vp++; + if (*vp) + *vp++ = '\0'; /* NULL terminate the key */ + p->type = NDBMNODE; + p->key = xstrdup (kp); + while (*vp && isspace ((unsigned char) *vp)) + vp++; /* skip whitespace to value */ + if (*vp == '\0') + { + if (!really_quiet) + error (0, 0, + "warning: NULL value for key `%s' at line %d of `%s'", + p->key, line_num, filename); + freenode (p); + continue; + } + p->data = xstrdup (vp); + if (addnode (list, p) == -1) + { + if (!really_quiet) + error (0, 0, + "duplicate key found for `%s' at line %d of `%s'", + p->key, line_num, filename); + freenode (p); + } + } + } + if (line_length < 0 && !feof (fp)) + /* FIXME: should give the name of the file. */ + error (0, errno, "cannot read file in mydbm_load_file"); + + free (line); + free (value); +} + +#endif /* MY_NDBM */ diff --git a/contrib/cvs-1.12.9/src/myndbm.h b/contrib/cvs-1.12.9/src/myndbm.h new file mode 100644 index 0000000000..83f9d7f07b --- /dev/null +++ b/contrib/cvs-1.12.9/src/myndbm.h @@ -0,0 +1,45 @@ +#ifdef MY_NDBM + +#define DBLKSIZ 4096 + +typedef struct +{ + List *dbm_list; /* cached database */ + Node *dbm_next; /* next key to return for nextkey() */ + + /* Name of the file to write to if modified is set. malloc'd. */ + char *name; + + /* Nonzero if the database has been modified and dbm_close needs to + write it out to disk. */ + int modified; +} DBM; + +typedef struct +{ + char *dptr; + int dsize; +} datum; + +/* + * So as not to conflict with other dbm_open, etc., routines that may + * be included by someone's libc, all of my emulation routines are prefixed + * by "my" and we define the "standard" ones to be "my" ones here. + */ +#define dbm_open mydbm_open +#define dbm_close mydbm_close +#define dbm_fetch mydbm_fetch +#define dbm_firstkey mydbm_firstkey +#define dbm_nextkey mydbm_nextkey +#define dbm_store mydbm_store +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +DBM *mydbm_open (char *file, int flags, int mode); +void mydbm_close (DBM * db); +datum mydbm_fetch (DBM * db, datum key); +datum mydbm_firstkey (DBM * db); +datum mydbm_nextkey (DBM * db); +extern int mydbm_store (DBM *, datum, datum, int); + +#endif /* MY_NDBM */ diff --git a/contrib/cvs-1.12.9/src/no_diff.c b/contrib/cvs-1.12.9/src/no_diff.c new file mode 100644 index 0000000000..35623e6c98 --- /dev/null +++ b/contrib/cvs-1.12.9/src/no_diff.c @@ -0,0 +1,90 @@ +/* + * 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 source distribution. + * + * No Difference + * + * The user file looks modified judging from its time stamp; however it needn't + * be. No_Difference() finds out whether it is or not. If it is not, it + * updates the administration. + * + * returns 0 if no differences are found and non-zero otherwise + */ + +#include "cvs.h" + +int +No_Difference (struct file_info *finfo, Vers_TS *vers) +{ + Node *p; + 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 */ + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If special files are in use, then any mismatch of file metadata + information also means that the files should be considered different. */ + if (preserve_perms && special_file_mismatch (finfo, vers->vn_user, NULL)) + return 1; +#endif + + if (vers->entdata && vers->entdata->options) + options = xstrdup (vers->entdata->options); + else + options = xstrdup (""); + + tocvsPath = wrap_tocvs_process_file (finfo->file); + retcode = RCS_cmp_file( vers->srcfile, vers->vn_user, (char **)NULL, + (char *)NULL, options, + tocvsPath == NULL ? finfo->file : tocvsPath ); + if (retcode == 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 + (finfo->file, finfo->update_dir, finfo->repository, SERVER_UPDATED); + } +#endif + free (ts); + + /* update the entdata pointer in the vers_ts structure */ + p = findnode (finfo->entries, finfo->file); + vers->entdata = 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. */ + TRACE ( 1, "unlink (%s)", tocvsPath); + if ( CVS_UNLINK (tocvsPath) < 0) + error (0, errno, "could not remove %s", tocvsPath); + } + + free (options); + return (ret); +} diff --git a/contrib/cvs-1.12.9/src/parseinfo.c b/contrib/cvs-1.12.9/src/parseinfo.c new file mode 100644 index 0000000000..f58da84da7 --- /dev/null +++ b/contrib/cvs-1.12.9/src/parseinfo.c @@ -0,0 +1,474 @@ +/* + * 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 source distribution. + */ + +#include "cvs.h" +#include "getline.h" + +extern char *logHistory; + + + +/* + * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for + * the first line in the file that matches the REPOSITORY, or if ALL != 0, any + * lines matching "ALL", or if no lines match, the last line matching + * "DEFAULT". + * + * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure. + */ +int +Parse_Info (const char *infofile, const char *repository, CALLPROC callproc, + int opt, void *closure) +{ + int err = 0; + FILE *fp_info; + char *infopath; + char *line = NULL; + size_t line_allocated = 0; + char *default_value = NULL; + int default_line = 0; + char *expanded_value; + int callback_done, line_number; + char *cp, *exp, *value; + const char *srepos; + const char *regex_err; + + if (current_parsed_root == NULL) + { + /* XXX - should be error maybe? */ + error (0, 0, "CVSROOT variable not set"); + return (1); + } + + /* find the info file and open it */ + infopath = xmalloc (strlen (current_parsed_root->directory) + + strlen (infofile) + + sizeof (CVSROOTADM) + + 3); + (void) sprintf (infopath, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, infofile); + 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); + free (infopath); + return 0; + } + + /* strip off the CVSROOT if repository was absolute */ + srepos = Short_Repository (repository); + + TRACE ( 1, "Parse_Info (%s, %s, %s)", + infopath, srepos, (opt & PIOPT_ALL) ? "ALL" : "not ALL"); + + /* search the info file for lines that match */ + callback_done = line_number = 0; + while (getline (&line, &line_allocated, fp_info) >= 0) + { + line_number++; + + /* skip lines starting with # */ + if (line[0] == '#') + continue; + + /* skip whitespace at beginning of line */ + for (cp = line; *cp && isspace ((unsigned char) *cp); cp++) + ; + + /* if *cp is null, the whole line was blank */ + if (*cp == '\0') + continue; + + /* the regular expression is everything up to the first space */ + for (exp = cp; *cp && !isspace ((unsigned char) *cp); cp++) + ; + if (*cp != '\0') + *cp++ = '\0'; + + /* skip whitespace up to the start of the matching value */ + while (*cp && isspace ((unsigned char) *cp)) + cp++; + + /* no value to match with the regular expression is an error */ + if (*cp == '\0') + { + error (0, 0, "syntax error at line %d file %s; ignored", + line_number, infofile); + continue; + } + value = cp; + + /* strip the newline off the end of the value */ + if ((cp = strrchr (value, '\n')) != NULL) + *cp = '\0'; + + /* + * At this point, exp points to the regular expression, and value + * points to the value to call the callback routine with. Evaluate + * the regular expression against srepos and callback with the value + * if it matches. + */ + + /* save the default value so we have it later if we need it */ + if (strcmp (exp, "DEFAULT") == 0) + { + if (default_value != NULL) + { + error (0, 0, "Multiple `DEFAULT' lines (%d and %d) in %s file", + default_line, line_number, infofile); + free (default_value); + } + default_value = xstrdup(value); + default_line = line_number; + continue; + } + + /* + * For a regular expression of "ALL", do the callback always We may + * execute lots of ALL callbacks in addition to *one* regular matching + * callback or default + */ + if (strcmp (exp, "ALL") == 0) + { + if (!(opt & PIOPT_ALL)) + error (0, 0, "Keyword `ALL' is ignored at line %d in %s file", + line_number, infofile); + else if ((expanded_value = expand_path (value, infofile, + line_number, 1)) + != NULL ) + { + err += callproc (repository, expanded_value, closure); + free (expanded_value); + } + else + err++; + continue; + } + + if (callback_done) + /* only first matching, plus "ALL"'s */ + continue; + + /* see if the repository matched this regular expression */ + if ((regex_err = re_comp (exp)) != NULL) + { + error (0, 0, "bad regular expression at line %d file %s: %s", + line_number, infofile, regex_err); + continue; + } + if (re_exec (srepos) == 0) + continue; /* no match */ + + /* it did, so do the callback and note that we did one */ + if( ( expanded_value = expand_path( value, infofile, line_number, 1 ) + ) != NULL ) + { + err += callproc( repository, expanded_value, closure ); + free (expanded_value); + } + else + err++; + callback_done = 1; + } + 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) + { + if( ( expanded_value = expand_path( default_value, infofile, + line_number, 1 ) + ) != NULL ) + { + err += callproc( repository, expanded_value, closure ); + free (expanded_value); + } + else + err++; + } + + /* free up space if necessary */ + if (default_value != NULL) + free (default_value); + free (infopath); + if (line != NULL) + free (line); + + return (err); +} + + +/* Parse the CVS config file. The syntax right now is a bit ad hoc + but tries to draw on the best or more common features of the other + *info files and various unix (or non-unix) config file syntaxes. + Lines starting with # are comments. Settings are lines of the form + KEYWORD=VALUE. There is currently no way to have a multi-line + VALUE (would be nice if there was, probably). + + CVSROOT is the $CVSROOT directory + (current_parsed_root->directory might not be set yet, so this + function takes the cvsroot as a function argument). + + Returns 0 for success, negative value for failure. Call + error(0, ...) on errors in addition to the return value. */ +int +parse_config (char *cvsroot) +{ + char *infopath; + FILE *fp_info; + char *line = NULL; + size_t line_allocated = 0; + size_t len; + char *p; + /* FIXME-reentrancy: If we do a multi-threaded server, this would need + to go to the per-connection data structures. */ + static int parsed = 0; + + /* Authentication code and serve_root might both want to call us. + Let this happen smoothly. */ + if (parsed) + return 0; + parsed = 1; + + infopath = xmalloc (strlen (cvsroot) + + sizeof (CVSROOTADM_CONFIG) + + sizeof (CVSROOTADM) + + 10); + if (infopath == NULL) + { + error (0, 0, "out of memory; cannot allocate infopath"); + goto error_return; + } + + strcpy (infopath, cvsroot); + strcat (infopath, "/"); + strcat (infopath, CVSROOTADM); + strcat (infopath, "/"); + strcat (infopath, CVSROOTADM_CONFIG); + + fp_info = CVS_FOPEN (infopath, "r"); + if (fp_info == NULL) + { + /* If no file, don't do anything special. */ + if (!existence_error (errno)) + { + /* Just a warning message; doesn't affect return + value, currently at least. */ + error (0, errno, "cannot open %s", infopath); + } + free (infopath); + return 0; + } + + while (getline (&line, &line_allocated, fp_info) >= 0) + { + /* Skip comments. */ + if (line[0] == '#') + continue; + + /* At least for the moment we don't skip whitespace at the start + of the line. Too picky? Maybe. But being insufficiently + picky leads to all sorts of confusion, and it is a lot easier + to start out picky and relax it than the other way around. + + Is there any kind of written standard for the syntax of this + sort of config file? Anywhere in POSIX for example (I guess + makefiles are sort of close)? Red Hat Linux has a bunch of + these too (with some GUI tools which edit them)... + + Along the same lines, we might want a table of keywords, + with various types (boolean, string, &c), as a mechanism + for making sure the syntax is consistent. Any good examples + to follow there (Apache?)? */ + + /* Strip the trailing newline. There will be one unless we + read a partial line without a newline, and then got end of + file (or error?). */ + + len = strlen (line) - 1; + if (line[len] == '\n') + line[len] = '\0'; + + /* Skip blank lines. */ + if (line[0] == '\0') + continue; + + /* The first '=' separates keyword from value. */ + p = strchr (line, '='); + if (p == NULL) + { + /* Probably should be printing line number. */ + error (0, 0, "syntax error in %s: line '%s' is missing '='", + infopath, line); + goto error_return; + } + + *p++ = '\0'; + + if (strcmp (line, "RCSBIN") == 0) + { + /* This option used to specify the directory for RCS + executables. But since we don't run them any more, + this is a noop. Silently ignore it so that a + repository can work with either new or old CVS. */ + ; + } + else if (strcmp (line, "SystemAuth") == 0) + { + if (strcmp (p, "no") == 0) +#ifdef AUTH_SERVER_SUPPORT + system_auth = 0; +#else + /* Still parse the syntax but ignore the + option. That way the same config file can + be used for local and server. */ + ; +#endif + else if (strcmp (p, "yes") == 0) +#ifdef AUTH_SERVER_SUPPORT + system_auth = 1; +#else + ; +#endif + else + { + error (0, 0, "unrecognized value '%s' for SystemAuth", p); + goto error_return; + } + } + else if (strcmp (line, "LocalKeyword") == 0) + { + RCS_setlocalid(p); + + } + else if (strcmp (line, "KeywordExpand") == 0) + { + RCS_setincexc(p); + + } + else if (strcmp (line, "PreservePermissions") == 0) + { + if (strcmp (p, "no") == 0) + preserve_perms = 0; + else if (strcmp (p, "yes") == 0) + { +#ifdef PRESERVE_PERMISSIONS_SUPPORT + preserve_perms = 1; +#else + error (0, 0, "\ +warning: this CVS does not support PreservePermissions"); +#endif + } + else + { + error (0, 0, "unrecognized value '%s' for PreservePermissions", + p); + goto error_return; + } + } + else if (strcmp (line, "TopLevelAdmin") == 0) + { + if (strcmp (p, "no") == 0) + top_level_admin = 0; + else if (strcmp (p, "yes") == 0) + top_level_admin = 1; + else + { + error (0, 0, "unrecognized value '%s' for TopLevelAdmin", p); + goto error_return; + } + } + else if (strcmp (line, "LockDir") == 0) + { + if (lock_dir != NULL) + free (lock_dir); + lock_dir = xstrdup (p); + /* Could try some validity checking, like whether we can + opendir it or something, but I don't see any particular + reason to do that now rather than waiting until lock.c. */ + } + else if (strcmp (line, "LogHistory") == 0) + { + if (strcmp (p, "all") != 0) + { + logHistory=xmalloc(strlen (p) + 1); + strcpy (logHistory, p); + } + } + else if (strcmp (line, "RereadLogAfterVerify") == 0) + { + if (strcmp (p, "no") == 0 || strcmp (p, "never") == 0) + RereadLogAfterVerify = LOGMSG_REREAD_NEVER; + else if (strcmp (p, "yes") == 0 || strcmp (p, "always") == 0) + RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS; + else if (strcmp (p, "stat") == 0) + RereadLogAfterVerify = LOGMSG_REREAD_STAT; + } + else if (strcmp (line, "UserAdminOptions") == 0) + { + UserAdminOptions = xmalloc(strlen(p) + 1); + strcpy(UserAdminOptions, p); + } +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + else if (strcmp (line, "UseNewInfoFmtStrings") == 0) + { + if (strcmp (p, "no") == 0) + UseNewInfoFmtStrings = 0; + else if (strcmp (p, "yes") == 0) + UseNewInfoFmtStrings = 1; + else + { + error (0, 0, "unrecognized value '%s' for UseNewInfoFmtStrings", p); + goto error_return; + } + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + else + { + /* We may be dealing with a keyword which was added in a + subsequent version of CVS. In that case it is a good idea + to complain, as (1) the keyword might enable a behavior like + alternate locking behavior, in which it is dangerous and hard + to detect if some CVS's have it one way and others have it + the other way, (2) in general, having us not do what the user + had in mind when they put in the keyword violates the + principle of least surprise. Note that one corollary is + adding new keywords to your CVSROOT/config file is not + particularly recommended unless you are planning on using + the new features. */ + error (0, 0, "%s: unrecognized keyword '%s'", + infopath, line); + goto error_return; + } + } + if (ferror (fp_info)) + { + error (0, errno, "cannot read %s", infopath); + goto error_return; + } + if (fclose (fp_info) < 0) + { + error (0, errno, "cannot close %s", infopath); + goto error_return; + } + free (infopath); + if (line != NULL) + free (line); + return 0; + + error_return: + if (infopath != NULL) + free (infopath); + if (line != NULL) + free (line); + return -1; +} diff --git a/contrib/cvs-1.12.9/src/patch.c b/contrib/cvs-1.12.9/src/patch.c new file mode 100644 index 0000000000..12ef8ab38f --- /dev/null +++ b/contrib/cvs-1.12.9/src/patch.c @@ -0,0 +1,805 @@ +/* + * 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 source distribution. + * + * Patch + * + * Create a Larry Wall format "patch" file between a previous release and the + * current head of a module, or between two releases. Can specify the + * release as either a date or a revision number. + */ + +#include "cvs.h" +#include "getline.h" + +static RETSIGTYPE patch_cleanup (void); +static Dtype patch_dirproc (void *callerdat, const char *dir, + const char *repos, const char *update_dir, + List *entries); +static int patch_fileproc (void *callerdat, struct file_info *finfo); +static int patch_proc (int argc, char **argv, char *xwhere, + char *mwhere, char *mfile, int shorten, + int local_specified, char *mname, char *msg); + +static int force_tag_match = 1; +static int patch_short = 0; +static int toptwo_diffs = 0; +static char *options = NULL; +static char *rev1 = NULL; +static int rev1_validated = 0; +static char *rev2 = NULL; +static int rev2_validated = 0; +static char *date1 = NULL; +static char *date2 = NULL; +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 [-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", + "\t-t\tTop two diffs - last change made to the file.\n", + "\t-D date\tDate.\n", + "\t-r rev\tRevision - symbolic or numeric.\n", + "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + + + +int +patch (int argc, char **argv) +{ + register int i; + int local = 0; + int c; + int err = 0; + DBM *db; + + if (argc == -1) + usage (patch_usage); + + optind = 0; + while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -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) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + cvs_cmd_name); + break; + case 'f': + force_tag_match = 0; + break; + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case 't': + toptwo_diffs = 1; + break; + case 's': + patch_short = 1; + break; + case 'D': + if (rev2 != NULL || date2 != NULL) + error (1, 0, + "no more than two revisions/dates can be specified"); + if (rev1 != NULL || date1 != NULL) + date2 = Make_Date (optarg); + else + date1 = Make_Date (optarg); + break; + case 'r': + if (rev2 != NULL || date2 != NULL) + error (1, 0, + "no more than two revisions/dates can be specified"); + if (rev1 != NULL || date1 != NULL) + rev2 = optarg; + else + rev1 = optarg; + break; + case 'k': + if (options) + free (options); + options = RCS_check_kflag (optarg); + break; + case 'V': + /* 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 */ + break; + case 'c': /* Context diff */ + unidiff = 0; + break; + case '?': + default: + usage (patch_usage); + break; + } + } + argc -= optind; + argv += optind; + + /* Sanity checks */ + if (argc < 1) + usage (patch_usage); + + if (toptwo_diffs && patch_short) + error (1, 0, "-t and -s options are mutually exclusive"); + if (toptwo_diffs && (date1 != NULL || date2 != NULL || + rev1 != NULL || rev2 != NULL)) + error (1, 0, "must not specify revisions/dates with -t option!"); + + if (!toptwo_diffs && (date1 == NULL && date2 == NULL && + rev1 == NULL && rev2 == NULL)) + error (1, 0, "must specify at least one revision/date!"); + if (date1 != NULL && date2 != NULL) + if (RCS_datecmp (date1, date2) >= 0) + error (1, 0, "second date must come after first date!"); + + /* if options is NULL, make it a NULL string */ + if (options == NULL) + options = xstrdup (""); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (local) + send_arg("-l"); + if (!force_tag_match) + send_arg("-f"); + if (toptwo_diffs) + send_arg("-t"); + if (patch_short) + send_arg("-s"); + if (unidiff) + send_arg("-u"); + + if (rev1) + option_with_arg ("-r", rev1); + if (date1) + client_senddate (date1); + if (rev2) + option_with_arg ("-r", rev2); + if (date2) + client_senddate (date2); + if (options[0] != '\0') + send_arg (options); + + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + } + + send_to_server ("rdiff\012", 0); + return get_responses_and_close (); + } +#endif + + /* clean up if we get a signal */ +#ifdef SIGABRT + (void)SIG_register (SIGABRT, patch_cleanup); +#endif +#ifdef SIGHUP + (void)SIG_register (SIGHUP, patch_cleanup); +#endif +#ifdef SIGINT + (void)SIG_register (SIGINT, patch_cleanup); +#endif +#ifdef SIGQUIT + (void)SIG_register (SIGQUIT, patch_cleanup); +#endif +#ifdef SIGPIPE + (void)SIG_register (SIGPIPE, patch_cleanup); +#endif +#ifdef SIGTERM + (void)SIG_register (SIGTERM, patch_cleanup); +#endif + + db = open_module (); + for (i = 0; i < argc; i++) + err += do_module (db, argv[i], PATCH, "Patching", patch_proc, + (char *)NULL, 0, local, 0, 0, (char *)NULL); + close_module (db); + free (options); + patch_cleanup (); + return err; +} + + + +/* + * callback proc for doing the real work of patching + */ +/* ARGSUSED */ +static int +patch_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile, + int shorten, int local_specified, char *mname, char *msg) +{ + char *myargv[2]; + int err = 0; + int which; + char *repository; + char *where; + + TRACE ( TRACE_FUNCTION, "patch_proc ( %s, %s, %s, %d, %d, %s, %s )", + xwhere ? xwhere : "(null)", + mwhere ? mwhere : "(null)", + mfile ? mfile : "(null)", + shorten, local_specified, + mname ? mname : "(null)", + msg ? msg : "(null)" ); + + repository = xmalloc (strlen (current_parsed_root->directory) + + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); + (void)sprintf (repository, "%s/%s", + current_parsed_root->directory, argv[0]); + where = xmalloc (strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + + 1); + (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; + + /* if the portion of the module is a path, put the dir part on repos */ + if ((cp = strrchr (mfile, '/')) != NULL) + { + *cp = '\0'; + (void)strcat (repository, "/"); + (void)strcat (repository, mfile); + (void)strcat (where, "/"); + (void)strcat (where, mfile); + mfile = cp + 1; + } + + /* take care of the rest */ + path = xmalloc (strlen (repository) + strlen (mfile) + 2); + (void)sprintf (path, "%s/%s", repository, mfile); + if (isdir (path)) + { + /* directory means repository gets the dir tacked on */ + (void)strcpy (repository, path); + (void)strcat (where, "/"); + (void)strcat (where, mfile); + } + else + { + myargv[0] = argv[0]; + myargv[1] = mfile; + argc = 2; + argv = myargv; + } + free (path); + } + + /* cd to the starting repository */ + if (CVS_CHDIR (repository) < 0) + { + error (0, errno, "cannot chdir to %s", repository); + free (repository); + return 1; + } + + if (force_tag_match) + which = W_REPOS | W_ATTIC; + else + which = W_REPOS; + + if (rev1 != NULL && !rev1_validated) + { + tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0, + repository); + rev1_validated = 1; + } + if (rev2 != NULL && !rev2_validated) + { + tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0, + repository); + rev2_validated = 1; + } + + /* start the recursion processor */ + err = start_recursion (patch_fileproc, NULL, patch_dirproc, NULL, NULL, + argc - 1, argv + 1, local_specified, + which, 0, CVS_LOCK_READ, where, 1, repository ); + free (repository); + free (where); + + return err; +} + + + +/* + * Called to examine a particular RCS file, as appropriate with the options + * that were set above. + */ +/* ARGSUSED */ +static int +patch_fileproc (void *callerdat, struct file_info *finfo) +{ + struct utimbuf t; + char *vers_tag, *vers_head; + char *rcs = NULL; + RCSNode *rcsfile; + FILE *fp1, *fp2, *fp3; + int ret = 0; + int isattic = 0; + int retcode = 0; + char *file1; + char *file2; + char *strippath; + char *line1, *line2; + size_t line1_chars_allocated; + size_t line2_chars_allocated; + char *cp1, *cp2; + FILE *fp; + int line_length; + + line1 = NULL; + line1_chars_allocated = 0; + line2 = NULL; + line2_chars_allocated = 0; + vers_tag = vers_head = NULL; + + /* find the parsed rcs file */ + if ((rcsfile = finfo->rcs) == NULL) + { + 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, + NULL); + if (vers_head != NULL && RCS_isdead (rcsfile, vers_head)) + { + free (vers_head); + vers_head = NULL; + } + } + + if (toptwo_diffs) + { + if (vers_head == NULL) + { + ret = 1; + goto out2; + } + + if (!date1) + date1 = xmalloc (MAXDATELEN); + *date1 = '\0'; + if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1) + { + if (!really_quiet) + error (0, 0, "cannot find date in rcs file %s revision %s", + rcs, vers_head); + ret = 1; + goto out2; + } + } + vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, NULL); + if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag)) + { + free (vers_tag); + vers_tag = NULL; + } + + if ((vers_tag == NULL && vers_head == NULL) || + (vers_tag != NULL && vers_head != NULL && + strcmp (vers_head, vers_tag) == 0)) + { + /* Nothing known about specified revs or + * not changed between releases. + */ + ret = 0; + goto out2; + } + + if (patch_short && (vers_tag == NULL || vers_head == NULL)) + { + /* For adds & removes with a short patch requested, we can print our + * error message now and get out. + */ + cvs_output ("File ", 0); + cvs_output (finfo->fullname, 0); + if (vers_tag == NULL) + { + cvs_output (" is new; ", 0); + cvs_output (rev2 ? rev2 : date2 ? date2 : "current", 0); + cvs_output (" revision ", 0); + cvs_output (vers_head, 0); + cvs_output ("\n", 1); + } + else + { + cvs_output (" is removed; ", 0); + cvs_output (rev1 ? rev1 : date1, 0); + cvs_output (" revision ", 0); + cvs_output (vers_tag, 0); + cvs_output ("\n", 1); + } + ret = 0; + goto out2; + } + + /* Create 3 empty files. I'm not really sure there is any advantage + * to doing so now rather than just waiting until later. + * + * There is - cvs_temp_file opens the file so that it can guarantee that + * we have exclusive write access to the file. Unfortunately we spoil that + * by closing it and reopening it again. Of course any better solution + * requires that the RCS functions accept open file pointers rather than + * simple file names. + */ + if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL) + { + error (0, errno, "cannot create temporary file %s", tmpfile1); + ret = 1; + goto out; + } + else + if (fclose (fp1) < 0) + error (0, errno, "warning: cannot close %s", tmpfile1); + if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL) + { + error (0, errno, "cannot create temporary file %s", tmpfile2); + ret = 1; + goto out; + } + else + if (fclose (fp2) < 0) + error (0, errno, "warning: cannot close %s", tmpfile2); + if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL) + { + error (0, errno, "cannot create temporary file %s", tmpfile3); + ret = 1; + goto out; + } + else + if (fclose (fp3) < 0) + error (0, errno, "warning: cannot close %s", tmpfile3); + + if (vers_tag != NULL) + { + retcode = RCS_checkout (rcsfile, NULL, vers_tag, rev1, options, + tmpfile1, NULL, NULL); + if (retcode != 0) + { + error (0, 0, + "cannot check out revision %s of %s", vers_tag, rcs); + ret = 1; + goto out; + } + memset ((char *) &t, 0, sizeof (t)); + if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag, + (char *) 0, 0)) != -1) + /* I believe this timestamp only affects the dates in our diffs, + and therefore should be on the server, not the client. */ + (void)utime (tmpfile1, &t); + } + else if (toptwo_diffs) + { + ret = 1; + goto out; + } + if (vers_head != NULL) + { + retcode = RCS_checkout (rcsfile, NULL, vers_head, rev2, options, + tmpfile2, NULL, NULL); + if (retcode != 0) + { + error (0, 0, + "cannot check out revision %s of %s", vers_head, rcs); + ret = 1; + goto out; + } + if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head, + (char *)0, 0)) != -1) + /* I believe this timestamp only affects the dates in our diffs, + and therefore should be on the server, not the client. */ + (void)utime (tmpfile2, &t); + } + + switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, unidiff ? "-u" : "-c", + tmpfile3)) + { + case -1: /* fork/wait failure */ + error (1, errno, "fork for diff failed on %s", rcs); + break; + case 0: /* nothing to do */ + break; + case 1: + /* + * The two revisions are really different, so read the first two + * lines of the diff output file, and munge them to include more + * reasonable file names that "patch" will understand, unless the + * user wanted a short patch. In that case, just output the short + * message. + */ + if (patch_short) + { + cvs_output ("File ", 0); + cvs_output (finfo->fullname, 0); + cvs_output (" changed from revision ", 0); + cvs_output (vers_tag, 0); + cvs_output (" to ", 0); + cvs_output (vers_head, 0); + cvs_output ("\n", 1); + ret = 0; + goto out; + } + + /* Output an "Index:" line for patch to use */ + cvs_output ("Index: ", 0); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); + + /* Now the munging. */ + fp = open_file (tmpfile3, "r"); + if (getline (&line1, &line1_chars_allocated, fp) < 0 || + getline (&line2, &line2_chars_allocated, fp) < 0) + { + if (feof (fp)) + error (0, 0, "\ +failed to read diff file header %s for %s: end of file", tmpfile3, rcs); + else + error (0, errno, + "failed to read diff file header %s for %s", + tmpfile3, rcs); + ret = 1; + if (fclose (fp) < 0) + error (0, errno, "error closing %s", tmpfile3); + goto out; + } + if (!unidiff) + { + if (strncmp (line1, "*** ", 4) != 0 || + strncmp (line2, "--- ", 4) != 0 || + (cp1 = strchr (line1, '\t')) == NULL || + (cp2 = strchr (line2, '\t')) == NULL) + { + error (0, 0, "invalid diff header for %s", rcs); + ret = 1; + if (fclose (fp) < 0) + error (0, errno, "error closing %s", tmpfile3); + goto out; + } + } + else + { + if (strncmp (line1, "--- ", 4) != 0 || + strncmp (line2, "+++ ", 4) != 0 || + (cp1 = strchr (line1, '\t')) == NULL || + (cp2 = strchr (line2, '\t')) == NULL) + { + error (0, 0, "invalid unidiff header for %s", rcs); + ret = 1; + if (fclose (fp) < 0) + error (0, errno, "error closing %s", tmpfile3); + goto out; + } + } + assert (current_parsed_root != NULL); + assert (current_parsed_root->directory != NULL); + { + strippath = xmalloc (strlen (current_parsed_root->directory) + + 2); + (void)sprintf (strippath, "%s/", + current_parsed_root->directory); + } + /*else + 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 + { + 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"); + + /* Note that the string "diff" is specified by POSIX (for -c) + and is part of the diff output format, not the name of a + program. */ + if (unidiff) + { + cvs_output ("diff -u ", 0); + cvs_output (file1, 0); + cvs_output (" ", 1); + cvs_output (file2, 0); + cvs_output ("\n", 1); + + cvs_output ("--- ", 0); + cvs_output (file1, 0); + cvs_output (cp1, 0); + cvs_output ("+++ ", 0); + } + else + { + cvs_output ("diff -c ", 0); + cvs_output (file1, 0); + cvs_output (" ", 1); + cvs_output (file2, 0); + cvs_output ("\n", 1); + + cvs_output ("*** ", 0); + cvs_output (file1, 0); + cvs_output (cp1, 0); + cvs_output ("--- ", 0); + } + + cvs_output (finfo->fullname, 0); + cvs_output (cp2, 0); + + /* spew the rest of the diff out */ + while ((line_length + = getline (&line1, &line1_chars_allocated, fp)) + >= 0) + cvs_output (line1, 0); + if (line_length < 0 && !feof (fp)) + error (0, errno, "cannot read %s", tmpfile3); + + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", tmpfile3); + free (file1); + free (file2); + break; + default: + error (0, 0, "diff failed for %s", finfo->fullname); + } + out: + if (line1) + free (line1); + if (line2) + free (line2); + if (CVS_UNLINK (tmpfile1) < 0) + error (0, errno, "cannot unlink %s", tmpfile1); + if (CVS_UNLINK (tmpfile2) < 0) + error (0, errno, "cannot unlink %s", tmpfile2); + if (CVS_UNLINK (tmpfile3) < 0) + error (0, errno, "cannot unlink %s", tmpfile3); + free (tmpfile1); + free (tmpfile2); + free (tmpfile3); + tmpfile1 = tmpfile2 = tmpfile3 = NULL; + + out2: + if (vers_tag != NULL) + free (vers_tag); + if (vers_head != NULL) + free (vers_head); + if (rcs != NULL) + free (rcs); + return ret; +} + + + +/* + * Print a warm fuzzy message + */ +/* ARGSUSED */ +static Dtype +patch_dirproc (void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries) +{ + if (!quiet) + error (0, 0, "Diffing %s", update_dir); + return R_PROCESS; +} + + + +/* + * Clean up temporary files + */ +static RETSIGTYPE +patch_cleanup (void) +{ + /* Note that the checks for existence_error are because we are + called from a signal handler, without SIG_begincrsect, so + we don't know whether the files got created. */ + + if (tmpfile1 != NULL) + { + if (unlink_file (tmpfile1) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", tmpfile1); + free (tmpfile1); + } + if (tmpfile2 != NULL) + { + if (unlink_file (tmpfile2) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", tmpfile2); + free (tmpfile2); + } + if (tmpfile3 != NULL) + { + if (unlink_file (tmpfile3) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", tmpfile3); + free (tmpfile3); + } + tmpfile1 = tmpfile2 = tmpfile3 = NULL; +} diff --git a/contrib/cvs-1.12.9/src/rcs.c b/contrib/cvs-1.12.9/src/rcs.c new file mode 100644 index 0000000000..c8e16760fe --- /dev/null +++ b/contrib/cvs-1.12.9/src/rcs.c @@ -0,0 +1,8494 @@ +/* + * Copyright (c) 1992, Brian Berliner and Jeff Polk + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * The routines contained in this file do all the rcs file parsing and + * manipulation + */ + +#include "cvs.h" +#include "edit.h" +#include "hardlink.h" + +/* These need to be source after cvs.h or HAVE_MMAP won't be set... */ +#ifdef HAVE_MMAP +# include +# ifndef HAVE_GETPAGESIZE +# include "getpagesize.h" +# endif +# ifndef MAP_FAILED +# define MAP_FAILED NULL +# endif +#endif + +int preserve_perms = 0; + +/* The RCS -k options, and a set of enums that must match the array. + These come first so that we can use enum kflag in function + prototypes. */ +static const char *const kflags[] = + {"kv", "kvl", "k", "v", "o", "b", (char *) NULL}; +enum kflag { KFLAG_KV = 0, KFLAG_KVL, KFLAG_K, KFLAG_V, KFLAG_O, KFLAG_B }; + +/* A structure we use to buffer the contents of an RCS file. The + various fields are only referenced directly by the rcsbuf_* + functions. We declare the struct here so that we can allocate it + on the stack, rather than in memory. */ + +struct rcsbuffer +{ + /* Points to the current position in the buffer. */ + char *ptr; + /* Points just after the last valid character in the buffer. */ + char *ptrend; + /* The file. */ + FILE *fp; + /* The name of the file, used for error messages. */ + const char *filename; + /* The starting file position of the data in the buffer. */ + unsigned long pos; + /* The length of the value. */ + size_t vlen; + /* Whether the value contains an '@' string. If so, we can not + compress whitespace characters. */ + int at_string; + /* The number of embedded '@' characters in an '@' string. If + this is non-zero, we must search the string for pairs of '@' + and convert them to a single '@'. */ + int embedded_at; +}; + +static RCSNode *RCS_parsercsfile_i (FILE * fp, const char *rcsfile); +static char *RCS_getdatebranch (RCSNode * rcs, const char *date, + const char *branch); +static void rcsbuf_open (struct rcsbuffer *, FILE *fp, + const char *filename, unsigned long pos); +static void rcsbuf_close (struct rcsbuffer *); +static int rcsbuf_getkey (struct rcsbuffer *, char **keyp, + char **valp); +static int rcsbuf_getrevnum (struct rcsbuffer *, char **revp); +static char *rcsbuf_fill (struct rcsbuffer *, char *ptr, char **keyp, + char **valp); +static int rcsbuf_valcmp (struct rcsbuffer *); +static char *rcsbuf_valcopy (struct rcsbuffer *, char *val, int polish, + size_t *lenp); +static void rcsbuf_valpolish (struct rcsbuffer *, char *val, int polish, + size_t *lenp); +static void rcsbuf_valpolish_internal (struct rcsbuffer *, char *to, + const char *from, size_t *lenp); +static off_t rcsbuf_ftello (struct rcsbuffer *); +static void rcsbuf_get_buffered (struct rcsbuffer *, char **datap, + size_t *lenp); +static void rcsbuf_cache (RCSNode *, struct rcsbuffer *); +static void rcsbuf_cache_close (void); +static void rcsbuf_cache_open (RCSNode *, off_t, FILE **, + struct rcsbuffer *); +static int checkmagic_proc (Node *p, void *closure); +static void do_branches (List * list, char *val); +static void do_symbols (List * list, char *val); +static void do_locks (List * list, char *val); +static void free_rcsnode_contents (RCSNode *); +static void free_rcsvers_contents (RCSVers *); +static void rcsvers_delproc (Node * p); +static char *translate_symtag (RCSNode *, const char *); +static char *RCS_addbranch (RCSNode *, const char *); +static char *truncate_revnum_in_place (char *); +static char *truncate_revnum (const char *); +static char *printable_date (const char *); +static char *escape_keyword_value (const char *, int *); +static void expand_keywords (RCSNode *, RCSVers *, const char *, + const char *, size_t, enum kflag, char *, + size_t, char **, size_t *); +static void cmp_file_buffer (void *, const char *, size_t); + +/* Routines for reading, parsing and writing RCS files. */ +static RCSVers *getdelta (struct rcsbuffer *, char *, char **, + char **); +static Deltatext *RCS_getdeltatext (RCSNode *, FILE *, + struct rcsbuffer *); +static void freedeltatext (Deltatext *); + +static void RCS_putadmin (RCSNode *, FILE *); +static void RCS_putdtree (RCSNode *, char *, FILE *); +static void RCS_putdesc (RCSNode *, FILE *); +static void putdelta (RCSVers *, FILE *); +static int putrcsfield_proc (Node *, void *); +static int putsymbol_proc (Node *, void *); +static void RCS_copydeltas (RCSNode *, FILE *, struct rcsbuffer *, + FILE *, Deltatext *, char *); +static int count_delta_actions (Node *, void *); +static void putdeltatext (FILE *, Deltatext *); + +static FILE *rcs_internal_lockfile (char *); +static void rcs_internal_unlockfile (FILE *, char *); +static char *rcs_lockfilename (const char *); + +/* The RCS file reading functions are called a lot, and they do some + string comparisons. This macro speeds things up a bit by skipping + the function call when the first characters are different. It + evaluates its arguments multiple times. */ +#define STREQ(a, b) (*(char *)(a) == *(char *)(b) && strcmp ((a), (b)) == 0) + +static char * getfullCVSname (char *, char **); + +/* + * We don't want to use isspace() from the C library because: + * + * 1. The definition of "whitespace" in RCS files includes ASCII + * backspace, but the C locale doesn't. + * 2. isspace is an very expensive function call in some implementations + * due to the addition of wide character support. + */ +static const char spacetab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, /* 0x00 - 0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0 - 0xff */ +}; + +#define whitespace(c) (spacetab[(unsigned char)c] != 0) + +static char *rcs_lockfile = NULL; +static int rcs_lockfd = -1; + + + +/* + * char * + * locate_rcs ( const char* file, const char *repository , int *inattic ) + * + * Find an RCS file in the repository, case insensitively when the cased name + * doesn't exist, we are running as the server, and a client has asked us to + * ignore case. + * + * Most parts of CVS will want to rely instead on RCS_parse which calls this + * function and is called by recurse.c which then puts the result in useful + * places like the rcs field of struct file_info. + * + * INPUTS + * + * repository the repository (including the directory) + * file the filename within that directory (without RCSEXT). + * inattic NULL or a pointer to the output boolean + * + * OUTPUTS + * + * inattic If this input was non-null, the destination will be + * set to true if the file was found in the attic or + * false if not. If no RCS file is found, this value + * is undefined. + * + * RETURNS + * + * a newly-malloc'd array containing the absolute pathname of the RCS + * file that was found or NULL when none was found. + * + * ERRORS + * + * errno can be set by the return value of the final call to + * locate_file_in_dir(). This should resolve to the system's existence error + * value (sometime ENOENT) if the Attic directory did not exist and ENOENT if + * the Attic was found but no matching files were found in the Attic or its + * parent. + */ +static char * +locate_rcs (const char *repository, const char *file, int *inattic) +{ + char *retval; + + /* First, try to find the file as cased. */ + retval = xmalloc (strlen (repository) + + sizeof (CVSATTIC) + + strlen (file) + + sizeof (RCSEXT) + + 3); + sprintf (retval, "%s/%s%s", repository, file, RCSEXT); + if (isreadable (retval)) + { + if (inattic) + *inattic = 0; + return retval; + } + sprintf (retval, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT); + if (isreadable (retval)) + { + if (inattic) + *inattic = 1; + return retval; + } + free (retval); + + return NULL; +} + + + +/* A few generic thoughts on error handling, in particular the + printing of unexpected characters that we find in the RCS file + (that is, why we use '\x%x' rather than %c or some such). + + * Avoiding %c means we don't have to worry about what is printable + and other such stuff. In error handling, often better to keep it + simple. + + * Hex rather than decimal or octal because character set standards + tend to use hex. + + * Saying "character 0x%x" might make it sound like we are printing + a file offset. So we use '\x%x'. + + * Would be nice to print the offset within the file, but I can + imagine various portability hassles (in particular, whether + unsigned long is always big enough to hold file offsets). */ + +/* Parse an rcsfile given a user file name and a repository. If there is + an error, we print an error message and return NULL. If the file + does not exist, we return NULL without printing anything (I'm not + sure this allows the caller to do anything reasonable, but it is + the current behavior). */ +RCSNode * +RCS_parse (const char *file, const char *repos) +{ + RCSNode *rcs; + FILE *fp; + RCSNode *retval = NULL; + char *rcsfile; + int inattic; + + /* We're creating a new RCSNode, so there is no hope of finding it + in the cache. */ + rcsbuf_cache_close (); + + if ((rcsfile = locate_rcs (repos, file, &inattic)) == NULL) + { + /* Handle the error cases */ + } + else if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) != NULL) + { + rcs = RCS_parsercsfile_i(fp, rcsfile); + if (rcs != NULL) + { + rcs->flags |= VALID; + if ( inattic ) + rcs->flags |= INATTIC; + } + + free ( rcsfile ); + retval = rcs; + } + else if (! existence_error (errno)) + { + free ( rcsfile ); + error (0, errno, "cannot open %s", rcsfile); + } + + return retval; +} + +/* + * Parse a specific rcsfile. + */ +RCSNode * +RCS_parsercsfile (const char *rcsfile) +{ + FILE *fp; + RCSNode *rcs; + + /* We're creating a new RCSNode, so there is no hope of finding it + in the cache. */ + rcsbuf_cache_close (); + + /* open the rcsfile */ + if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) == NULL) + { + error (0, errno, "Couldn't open rcs file `%s'", rcsfile); + return (NULL); + } + + rcs = RCS_parsercsfile_i (fp, rcsfile); + + return (rcs); +} + + + +/* + */ +static RCSNode * +RCS_parsercsfile_i (FILE *fp, const char *rcsfile) +{ + RCSNode *rdata; + struct rcsbuffer rcsbuf; + char *key, *value; + + /* make a node */ + rdata = (RCSNode *) xmalloc (sizeof (RCSNode)); + memset ((char *)rdata, 0, sizeof (RCSNode)); + rdata->refcount = 1; + rdata->path = xstrdup (rcsfile); + + /* Process HEAD, BRANCH, and EXPAND keywords from the RCS header. + + Most cvs operations on the main branch don't need any more + information. Those that do call RCS_reparsercsfile to parse + the rest of the header and the deltas. */ + + rcsbuf_open (&rcsbuf, fp, rcsfile, 0); + + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + goto l_error; + if (STREQ (key, RCSDESC)) + goto l_error; + + if (STREQ (RCSHEAD, key) && value != NULL) + rdata->head = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *)NULL); + + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + goto l_error; + if (STREQ (key, RCSDESC)) + goto l_error; + + if (STREQ (RCSBRANCH, key) && value != NULL) + { + char *cp; + + rdata->branch = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *)NULL); + if ((numdots (rdata->branch) & 1) != 0) + { + /* turn it into a branch if it's a revision */ + cp = strrchr (rdata->branch, '.'); + *cp = '\0'; + } + } + + /* Look ahead for expand, stopping when we see desc or a revision + number. */ + while (1) + { + char *cp; + + if (STREQ (RCSEXPAND, key)) + { + rdata->expand = rcsbuf_valcopy (&rcsbuf, value, 0, + (size_t *)NULL); + break; + } + + for (cp = key; + (isdigit ((unsigned char)*cp) || *cp == '.') && *cp != '\0'; + cp++) + /* do nothing */ ; + if (*cp == '\0') + break; + + if (STREQ (RCSDESC, key)) + break; + + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + break; + } + + rdata->flags |= PARTIAL; + + rcsbuf_cache (rdata, &rcsbuf); + + return rdata; + +l_error: + error (0, 0, "`%s' does not appear to be a valid rcs file", + rcsfile); + rcsbuf_close (&rcsbuf); + freercsnode (&rdata); + fclose (fp); + return NULL; +} + + + +/* Do the real work of parsing an RCS file. + + On error, die with a fatal error; if it returns at all it was successful. + + If PFP is NULL, close the file when done. Otherwise, leave it open + and store the FILE * in *PFP. */ +void +RCS_reparsercsfile (RCSNode *rdata, FILE **pfp, struct rcsbuffer *rcsbufp) +{ + FILE *fp; + char *rcsfile; + struct rcsbuffer rcsbuf; + Node *q, *kv; + RCSVers *vnode; + int gotkey; + char *cp; + char *key, *value; + + assert (rdata != NULL); + rcsfile = rdata->path; + + rcsbuf_cache_open (rdata, 0, &fp, &rcsbuf); + + /* make a node */ + /* This probably shouldn't be done until later: if a file has an + empty revision tree (which is permissible), rdata->versions + should be NULL. -twp */ + rdata->versions = getlist (); + + /* + * process all the special header information, break out when we get to + * the first revision delta + */ + gotkey = 0; + for (;;) + { + /* get the next key/value pair */ + if (!gotkey) + { + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + { + error (1, 0, "`%s' does not appear to be a valid rcs file", + rcsfile); + } + } + + gotkey = 0; + + /* Skip head, branch and expand tags; we already have them. */ + if (STREQ (key, RCSHEAD) + || STREQ (key, RCSBRANCH) + || STREQ (key, RCSEXPAND)) + { + continue; + } + + if (STREQ (key, "access")) + { + if (value != NULL) + { + /* We pass the POLISH parameter as 1 because + RCS_addaccess expects nothing but spaces. FIXME: + It would be easy and more efficient to change + RCS_addaccess. */ + rdata->access = rcsbuf_valcopy (&rcsbuf, value, 1, + (size_t *) NULL); + } + continue; + } + + /* We always save lock information, so that we can handle + -kkvl correctly when checking out a file. */ + if (STREQ (key, "locks")) + { + if (value != NULL) + rdata->locks_data = rcsbuf_valcopy (&rcsbuf, value, 0, + (size_t *) NULL); + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + { + error (1, 0, "premature end of file reading %s", rcsfile); + } + if (STREQ (key, "strict") && value == NULL) + { + rdata->strict_locks = 1; + } + else + gotkey = 1; + continue; + } + + if (STREQ (RCSSYMBOLS, key)) + { + if (value != NULL) + rdata->symbols_data = rcsbuf_valcopy (&rcsbuf, value, 0, + (size_t *) NULL); + continue; + } + + /* + * check key for '.''s and digits (probably a rev) if it is a + * revision or `desc', we are done with the headers and are down to the + * revision deltas, so we break out of the loop + */ + for (cp = key; + (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0'; + cp++) + /* do nothing */ ; + /* Note that when comparing with RCSDATE, we are not massaging + VALUE from the string found in the RCS file. This is OK + since we know exactly what to expect. */ + if (*cp == '\0' && strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) == 0) + break; + + if (STREQ (key, RCSDESC)) + break; + + if (STREQ (key, "comment")) + { + rdata->comment = rcsbuf_valcopy (&rcsbuf, value, 0, + (size_t *) NULL); + continue; + } + if (rdata->other == NULL) + rdata->other = getlist (); + kv = getnode (); + kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD; + kv->key = xstrdup (key); + kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD, + (size_t *) NULL); + if (addnode (rdata->other, kv) != 0) + { + error (0, 0, "warning: duplicate key `%s' in RCS file `%s'", + key, rcsfile); + freenode (kv); + } + + /* if we haven't grabbed it yet, we didn't want it */ + } + + /* We got out of the loop, so we have the first part of the first + revision delta in KEY (the revision) and VALUE (the date key + and its value). This is what getdelta expects to receive. */ + + while ((vnode = getdelta (&rcsbuf, rcsfile, &key, &value)) != NULL) + { + /* get the node */ + q = getnode (); + q->type = RCSVERS; + q->delproc = rcsvers_delproc; + q->data = vnode; + q->key = vnode->version; + + /* add the nodes to the list */ + if (addnode (rdata->versions, q) != 0) + { +#if 0 + purify_printf("WARNING: Adding duplicate version: %s (%s)\n", + q->key, rcsfile); + freenode (q); +#endif + } + } + + /* Here KEY and VALUE are whatever caused getdelta to return NULL. */ + + if (STREQ (key, RCSDESC)) + { + if (rdata->desc != NULL) + { + error (0, 0, + "warning: duplicate key `%s' in RCS file `%s'", + key, rcsfile); + free (rdata->desc); + } + rdata->desc = rcsbuf_valcopy (&rcsbuf, value, 1, (size_t *) NULL); + } + + rdata->delta_pos = rcsbuf_ftello (&rcsbuf); + + if (pfp == NULL) + rcsbuf_cache (rdata, &rcsbuf); + else + { + *pfp = fp; + *rcsbufp = rcsbuf; + } + rdata->flags &= ~PARTIAL; +} + +/* Move RCS into or out of the Attic, depending on TOATTIC. If the + file is already in the desired place, return without doing + anything. At some point may want to think about how this relates + to RCS_rewrite but that is a bit hairy (if one wants renames to be + atomic, or that kind of thing). If there is an error, print a message + and return 1. On success, return 0. */ +int +RCS_setattic (RCSNode *rcs, int toattic) +{ + char *newpath; + const char *p; + char *q; + + /* Some systems aren't going to let us rename an open file. */ + rcsbuf_cache_close (); + + /* Could make the pathname computations in this file, and probably + in other parts of rcs.c too, easier if the REPOS and FILE + arguments to RCS_parse got stashed in the RCSNode. */ + + if (toattic) + { + mode_t omask; + + if (rcs->flags & INATTIC) + return 0; + + /* Example: rcs->path is "/foo/bar/baz,v". */ + newpath = xmalloc (strlen (rcs->path) + sizeof CVSATTIC + 5); + p = last_component (rcs->path); + strncpy (newpath, rcs->path, p - rcs->path); + strcpy (newpath + (p - rcs->path), CVSATTIC); + + /* Create the Attic directory if it doesn't exist. */ + omask = umask (cvsumask); + if (CVS_MKDIR (newpath, 0777) < 0 && errno != EEXIST) + error (0, errno, "cannot make directory %s", newpath); + (void) umask (omask); + + strcat (newpath, "/"); + strcat (newpath, p); + + if (CVS_RENAME (rcs->path, newpath) < 0) + { + int save_errno = errno; + + /* The checks for isreadable look awfully fishy, but + I'm going to leave them here for now until I + can think harder about whether they take care of + some cases which should be handled somehow. */ + + if (isreadable (rcs->path) || !isreadable (newpath)) + { + error (0, save_errno, "cannot rename %s to %s", + rcs->path, newpath); + free (newpath); + return 1; + } + } + } + else + { + if (!(rcs->flags & INATTIC)) + return 0; + + newpath = xmalloc (strlen (rcs->path)); + + /* Example: rcs->path is "/foo/bar/Attic/baz,v". */ + p = last_component (rcs->path); + strncpy (newpath, rcs->path, p - rcs->path - 1); + newpath[p - rcs->path - 1] = '\0'; + q = newpath + (p - rcs->path - 1) - (sizeof CVSATTIC - 1); + assert (strncmp (q, CVSATTIC, sizeof CVSATTIC - 1) == 0); + strcpy (q, p); + + if (CVS_RENAME (rcs->path, newpath) < 0) + { + error (0, errno, "failed to move `%s' out of the attic", + rcs->path); + free (newpath); + return 1; + } + } + + free (rcs->path); + rcs->path = newpath; + + return 0; +} + +/* + * Fully parse the RCS file. Store all keyword/value pairs, fetch the + * log messages for each revision, and fetch add and delete counts for + * each revision (we could fetch the entire text for each revision, + * but the only caller, log_fileproc, doesn't need that information, + * so we don't waste the memory required to store it). The add and + * delete counts are stored on the OTHER field of the RCSVERSNODE + * structure, under the names ";add" and ";delete", so that we don't + * waste the memory space of extra fields in RCSVERSNODE for code + * which doesn't need this information. + */ + +void +RCS_fully_parse (RCSNode *rcs) +{ + FILE *fp; + struct rcsbuffer rcsbuf; + + RCS_reparsercsfile (rcs, &fp, &rcsbuf); + + while (1) + { + char *key, *value; + Node *vers; + RCSVers *vnode; + + /* Rather than try to keep track of how much information we + have read, just read to the end of the file. */ + if (!rcsbuf_getrevnum (&rcsbuf, &key)) + break; + + vers = findnode (rcs->versions, key); + if (vers == NULL) + error (1, 0, + "mismatch in rcs file %s between deltas and deltatexts (%s)", + rcs->path, key); + + vnode = vers->data; + + while (rcsbuf_getkey (&rcsbuf, &key, &value)) + { + if (!STREQ (key, "text")) + { + Node *kv; + + if (vnode->other == NULL) + vnode->other = getlist (); + kv = getnode (); + kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD; + kv->key = xstrdup (key); + kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD, + (size_t *)NULL); + if (addnode (vnode->other, kv) != 0) + { + error (0, 0, + "\ +warning: duplicate key `%s' in version `%s' of RCS file `%s'", + key, vnode->version, rcs->path); + freenode (kv); + } + + continue; + } + + if (!STREQ (vnode->version, rcs->head)) + { + unsigned long add, del; + char buf[50]; + Node *kv; + + /* This is a change text. Store the add and delete + counts. */ + add = 0; + del = 0; + if (value != NULL) + { + size_t vallen; + const char *cp; + + rcsbuf_valpolish (&rcsbuf, value, 0, &vallen); + cp = value; + while (cp < value + vallen) + { + char op; + unsigned long count; + + op = *cp++; + if (op != 'a' && op != 'd') + error (1, 0, "\ +unrecognized operation '\\x%x' in %s", + op, rcs->path); + (void) strtoul (cp, (char **) &cp, 10); + if (*cp++ != ' ') + error (1, 0, "space expected in %s revision %s", + rcs->path, vnode->version); + count = strtoul (cp, (char **) &cp, 10); + if (*cp++ != '\012') + error (1, 0, "linefeed expected in %s revision %s", + rcs->path, vnode->version); + + if (op == 'd') + del += count; + else + { + add += count; + while (count != 0) + { + if (*cp == '\012') + --count; + else if (cp == value + vallen) + { + if (count != 1) + error (1, 0, "\ +premature end of value in %s revision %s", + rcs->path, vnode->version); + else + break; + } + ++cp; + } + } + } + } + + sprintf (buf, "%lu", add); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (";add"); + kv->data = xstrdup (buf); + if (addnode (vnode->other, kv) != 0) + { + error (0, 0, + "\ +warning: duplicate key `%s' in version `%s' of RCS file `%s'", + key, vnode->version, rcs->path); + freenode (kv); + } + + sprintf (buf, "%lu", del); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (";delete"); + kv->data = xstrdup (buf); + if (addnode (vnode->other, kv) != 0) + { + error (0, 0, + "\ +warning: duplicate key `%s' in version `%s' of RCS file `%s'", + key, vnode->version, rcs->path); + freenode (kv); + } + } + + /* We have found the "text" key which ends the data for + this revision. Break out of the loop and go on to the + next revision. */ + break; + } + } + + rcsbuf_cache (rcs, &rcsbuf); +} + + + +/* + * freercsnode - free up the info for an RCSNode + */ +void +freercsnode (RCSNode **rnodep) +{ + if (rnodep == NULL || *rnodep == NULL) + return; + + ((*rnodep)->refcount)--; + if ((*rnodep)->refcount != 0) + { + *rnodep = (RCSNode *) NULL; + return; + } + free ((*rnodep)->path); + if ((*rnodep)->head != (char *) NULL) + free ((*rnodep)->head); + if ((*rnodep)->branch != (char *) NULL) + free ((*rnodep)->branch); + free_rcsnode_contents (*rnodep); + free ((char *) *rnodep); + *rnodep = (RCSNode *) NULL; +} + +/* + * free_rcsnode_contents - free up the contents of an RCSNode without + * freeing the node itself, or the file name, or the head, or the + * path. This returns the RCSNode to the state it is in immediately + * after a call to RCS_parse. + */ +static void +free_rcsnode_contents (RCSNode *rnode) +{ + dellist (&rnode->versions); + if (rnode->symbols != (List *) NULL) + dellist (&rnode->symbols); + if (rnode->symbols_data != (char *) NULL) + free (rnode->symbols_data); + if (rnode->expand != NULL) + free (rnode->expand); + if (rnode->other != (List *) NULL) + dellist (&rnode->other); + if (rnode->access != NULL) + free (rnode->access); + if (rnode->locks_data != NULL) + free (rnode->locks_data); + if (rnode->locks != (List *) NULL) + dellist (&rnode->locks); + if (rnode->comment != NULL) + free (rnode->comment); + if (rnode->desc != NULL) + free (rnode->desc); +} + +/* free_rcsvers_contents -- free up the contents of an RCSVers node, + but also free the pointer to the node itself. */ +/* Note: The `hardlinks' list is *not* freed, since it is merely a + pointer into the `hardlist' structure (defined in hardlink.c), and + that structure is freed elsewhere in the program. */ + +static void +free_rcsvers_contents (RCSVers *rnode) +{ + if (rnode->branches != (List *) NULL) + dellist (&rnode->branches); + if (rnode->date != (char *) NULL) + free (rnode->date); + if (rnode->next != (char *) NULL) + free (rnode->next); + if (rnode->author != (char *) NULL) + free (rnode->author); + if (rnode->state != (char *) NULL) + free (rnode->state); + if (rnode->other != (List *) NULL) + dellist (&rnode->other); + if (rnode->other_delta != NULL) + dellist (&rnode->other_delta); + if (rnode->text != NULL) + freedeltatext (rnode->text); + free ((char *) rnode); +} + +/* + * rcsvers_delproc - free up an RCSVers type node + */ +static void +rcsvers_delproc (Node *p) +{ + free_rcsvers_contents (p->data); +} + +/* These functions retrieve keys and values from an RCS file using a + buffer. We use this somewhat complex approach because it turns out + that for many common operations, CVS spends most of its time + reading keys, so it's worth doing some fairly hairy optimization. */ + +/* The number of bytes we try to read each time we need more data. */ + +#define RCSBUF_BUFSIZE (8192) + +/* The buffer we use to store data. This grows as needed. */ + +static char *rcsbuf_buffer = NULL; +static size_t rcsbuf_buffer_size = 0; + +/* Whether rcsbuf_buffer is in use. This is used as a sanity check. */ + +static int rcsbuf_inuse; + +/* Set up to start gathering keys and values from an RCS file. This + initializes RCSBUF. */ + +static void +rcsbuf_open (struct rcsbuffer *rcsbuf, FILE *fp, const char *filename, long unsigned int pos) +{ + if (rcsbuf_inuse) + error (1, 0, "rcsbuf_open: internal error"); + rcsbuf_inuse = 1; + +#ifdef HAVE_MMAP + { + /* When we have mmap, it is much more efficient to let the system do the + * buffering and caching for us + */ + struct stat fs; + size_t mmap_off = 0; + + if ( fstat (fileno(fp), &fs) < 0 ) + error ( 1, errno, "Could not stat RCS archive %s for mapping", filename ); + + if (pos) + { + size_t ps = getpagesize (); + mmap_off = ( pos / ps ) * ps; + } + + /* Map private here since this particular buffer is read only */ + rcsbuf_buffer = mmap ( NULL, fs.st_size - mmap_off, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, fileno(fp), mmap_off ); + if ( rcsbuf_buffer == NULL || rcsbuf_buffer == MAP_FAILED ) + error ( 1, errno, "Could not map memory to RCS archive %s", filename ); + + rcsbuf_buffer_size = fs.st_size - mmap_off; + rcsbuf->ptr = rcsbuf_buffer + pos - mmap_off; + rcsbuf->ptrend = rcsbuf_buffer + fs.st_size - mmap_off; + rcsbuf->pos = mmap_off; + } +#else /* HAVE_MMAP */ + if (rcsbuf_buffer_size < RCSBUF_BUFSIZE) + expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, RCSBUF_BUFSIZE); + + rcsbuf->ptr = rcsbuf_buffer; + rcsbuf->ptrend = rcsbuf_buffer; + rcsbuf->pos = pos; +#endif /* HAVE_MMAP */ + rcsbuf->fp = fp; + rcsbuf->filename = filename; + rcsbuf->vlen = 0; + rcsbuf->at_string = 0; + rcsbuf->embedded_at = 0; +} + +/* Stop gathering keys from an RCS file. */ + +static void +rcsbuf_close (struct rcsbuffer *rcsbuf) +{ + if (! rcsbuf_inuse) + error (1, 0, "rcsbuf_close: internal error"); +#ifdef HAVE_MMAP + munmap ( rcsbuf_buffer, rcsbuf_buffer_size ); +#endif + rcsbuf_inuse = 0; +} + +/* Read a key/value pair from an RCS file. This sets *KEYP to point + to the key, and *VALUEP to point to the value. A missing or empty + value is indicated by setting *VALUEP to NULL. + + This function returns 1 on success, or 0 on EOF. If there is an + error reading the file, or an EOF in an unexpected location, it + gives a fatal error. + + This sets *KEYP and *VALUEP to point to storage managed by + rcsbuf_getkey. Moreover, *VALUEP has not been massaged from the + RCS format: it may contain embedded whitespace and embedded '@' + characters. Call rcsbuf_valcopy or rcsbuf_valpolish to do + appropriate massaging. */ + +/* Note that the extreme hair in rcsbuf_getkey is because profiling + statistics show that it was worth it. */ + +static int +rcsbuf_getkey (struct rcsbuffer *rcsbuf, char **keyp, char **valp) +{ + register const char * const my_spacetab = spacetab; + register char *ptr, *ptrend; + char c; + +#define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0) + + rcsbuf->vlen = 0; + rcsbuf->at_string = 0; + rcsbuf->embedded_at = 0; + + ptr = rcsbuf->ptr; + ptrend = rcsbuf->ptrend; + + /* Sanity check. */ + assert (ptr >= rcsbuf_buffer && ptr <= rcsbuf_buffer + rcsbuf_buffer_size); + assert (ptrend >= rcsbuf_buffer && ptrend <= rcsbuf_buffer + rcsbuf_buffer_size); + +#ifndef HAVE_MMAP + /* If the pointer is more than RCSBUF_BUFSIZE bytes into the + buffer, move back to the start of the buffer. This keeps the + buffer from growing indefinitely. */ + if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE) + { + int len; + + len = ptrend - ptr; + + /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes + at a time, so we can't have more bytes than that past PTR. */ + assert (len <= RCSBUF_BUFSIZE); + + /* Update the POS field, which holds the file offset of the + first byte in the RCSBUF_BUFFER buffer. */ + rcsbuf->pos += ptr - rcsbuf_buffer; + + memcpy (rcsbuf_buffer, ptr, len); + ptr = rcsbuf_buffer; + ptrend = ptr + len; + rcsbuf->ptrend = ptrend; + } +#endif /* HAVE_MMAP */ + + /* Skip leading whitespace. */ + + while (1) + { + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); + if (ptr == NULL) + return 0; + ptrend = rcsbuf->ptrend; + } + + c = *ptr; + if (! my_whitespace (c)) + break; + + ++ptr; + } + + /* We've found the start of the key. */ + + *keyp = ptr; + + if (c != ';') + { + while (1) + { + ++ptr; + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, keyp, (char **) NULL); + if (ptr == NULL) + error (1, 0, "EOF in key in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + c = *ptr; + if (c == ';' || my_whitespace (c)) + break; + } + } + + /* Here *KEYP points to the key in the buffer, C is the character + we found at the of the key, and PTR points to the location in + the buffer where we found C. We must set *PTR to \0 in order + to terminate the key. If the key ended with ';', then there is + no value. */ + + *ptr = '\0'; + ++ptr; + + if (c == ';') + { + *valp = NULL; + rcsbuf->ptr = ptr; + return 1; + } + + /* C must be whitespace. Skip whitespace between the key and the + value. If we find ';' now, there is no value. */ + + while (1) + { + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, keyp, (char **) NULL); + if (ptr == NULL) + error (1, 0, "EOF while looking for value in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + c = *ptr; + if (c == ';') + { + *valp = NULL; + rcsbuf->ptr = ptr + 1; + return 1; + } + if (! my_whitespace (c)) + break; + ++ptr; + } + + /* Now PTR points to the start of the value, and C is the first + character of the value. */ + + if (c != '@') + *valp = ptr; + else + { + char *pat; + size_t vlen; + + /* Optimize the common case of a value composed of a single + '@' string. */ + + rcsbuf->at_string = 1; + + ++ptr; + + *valp = ptr; + + while (1) + { + while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL) + { + /* Note that we pass PTREND as the PTR value to + rcsbuf_fill, so that we will wind up setting PTR to + the location corresponding to the old PTREND, so + that we don't search the same bytes again. */ + ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); + if (ptr == NULL) + error (1, 0, + "EOF while looking for end of string in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + /* Handle the special case of an '@' right at the end of + the known bytes. */ + if (pat + 1 >= ptrend) + { + /* Note that we pass PAT, not PTR, here. */ + pat = rcsbuf_fill (rcsbuf, pat, keyp, valp); + if (pat == NULL) + { + /* EOF here is OK; it just means that the last + character of the file was an '@' terminating a + value for a key type which does not require a + trailing ';'. */ + pat = rcsbuf->ptrend - 1; + + } + ptrend = rcsbuf->ptrend; + + /* Note that the value of PTR is bogus here. This is + OK, because we don't use it. */ + } + + if (pat + 1 >= ptrend || pat[1] != '@') + break; + + /* We found an '@' pair in the string. Keep looking. */ + ++rcsbuf->embedded_at; + ptr = pat + 2; + } + + /* Here PAT points to the final '@' in the string. */ + + *pat = '\0'; + + vlen = pat - *valp; + if (vlen == 0) + *valp = NULL; + rcsbuf->vlen = vlen; + + ptr = pat + 1; + } + + /* Certain keywords only have a '@' string. If there is no '@' + string, then the old getrcskey function assumed that they had + no value, and we do the same. */ + + { + char *k; + + k = *keyp; + if (STREQ (k, RCSDESC) + || STREQ (k, "text") + || STREQ (k, "log")) + { + if (c != '@') + *valp = NULL; + rcsbuf->ptr = ptr; + return 1; + } + } + + /* If we've already gathered a '@' string, try to skip whitespace + and find a ';'. */ + if (c == '@') + { + while (1) + { + char n; + + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp); + if (ptr == NULL) + error (1, 0, "EOF in value in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + n = *ptr; + if (n == ';') + { + /* We're done. We already set everything up for this + case above. */ + rcsbuf->ptr = ptr + 1; + return 1; + } + if (! my_whitespace (n)) + break; + ++ptr; + } + + /* The value extends past the '@' string. We need to undo the + '@' stripping done in the default case above. This + case never happens in a plain RCS file, but it can happen + if user defined phrases are used. */ + ((*valp)--)[rcsbuf->vlen++] = '@'; + } + + /* Here we have a value which is not a simple '@' string. We need + to gather up everything until the next ';', including any '@' + strings. *VALP points to the start of the value. If + RCSBUF->VLEN is not zero, then we have already read an '@' + string, and PTR points to the data following the '@' string. + Otherwise, PTR points to the start of the value. */ + + while (1) + { + char *start, *psemi, *pat; + + /* Find the ';' which must end the value. */ + start = ptr; + while ((psemi = memchr (ptr, ';', ptrend - ptr)) == NULL) + { + int slen; + + /* Note that we pass PTREND as the PTR value to + rcsbuf_fill, so that we will wind up setting PTR to the + location corresponding to the old PTREND, so that we + don't search the same bytes again. */ + slen = start - *valp; + ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); + if (ptr == NULL) + error (1, 0, "EOF in value in RCS file %s", rcsbuf->filename); + start = *valp + slen; + ptrend = rcsbuf->ptrend; + } + + /* See if there are any '@' strings in the value. */ + pat = memchr (start, '@', psemi - start); + + if (pat == NULL) + { + size_t vlen; + + /* We're done with the value. Trim any trailing + whitespace. */ + + rcsbuf->ptr = psemi + 1; + + start = *valp; + while (psemi > start && my_whitespace (psemi[-1])) + --psemi; + *psemi = '\0'; + + vlen = psemi - start; + if (vlen == 0) + *valp = NULL; + rcsbuf->vlen = vlen; + + return 1; + } + + /* We found an '@' string in the value. We set RCSBUF->AT_STRING + and RCSBUF->EMBEDDED_AT to indicate that we won't be able to + compress whitespace correctly for this type of value. + Since this type of value never arises in a normal RCS file, + this should not be a big deal. It means that if anybody + adds a phrase which can have both an '@' string and regular + text, they will have to handle whitespace compression + themselves. */ + + rcsbuf->at_string = 1; + rcsbuf->embedded_at = -1; + + ptr = pat + 1; + + while (1) + { + while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL) + { + /* Note that we pass PTREND as the PTR value to + rcsbuff_fill, so that we will wind up setting PTR + to the location corresponding to the old PTREND, so + that we don't search the same bytes again. */ + ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); + if (ptr == NULL) + error (1, 0, + "EOF while looking for end of string in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + /* Handle the special case of an '@' right at the end of + the known bytes. */ + if (pat + 1 >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp); + if (ptr == NULL) + error (1, 0, "EOF in value in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + if (pat[1] != '@') + break; + + /* We found an '@' pair in the string. Keep looking. */ + ptr = pat + 2; + } + + /* Here PAT points to the final '@' in the string. */ + ptr = pat + 1; + } + +#undef my_whitespace +} + +/* Read an RCS revision number from an RCS file. This sets *REVP to + point to the revision number; it will point to space that is + managed by the rcsbuf functions, and is only good until the next + call to rcsbuf_getkey or rcsbuf_getrevnum. + + This function returns 1 on success, or 0 on EOF. If there is an + error reading the file, or an EOF in an unexpected location, it + gives a fatal error. */ + +static int +rcsbuf_getrevnum (struct rcsbuffer *rcsbuf, char **revp) +{ + char *ptr, *ptrend; + char c; + + ptr = rcsbuf->ptr; + ptrend = rcsbuf->ptrend; + + *revp = NULL; + + /* Skip leading whitespace. */ + + while (1) + { + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); + if (ptr == NULL) + return 0; + ptrend = rcsbuf->ptrend; + } + + c = *ptr; + if (! whitespace (c)) + break; + + ++ptr; + } + + if (! isdigit ((unsigned char) c) && c != '.') + error (1, 0, + "\ +unexpected '\\x%x' reading revision number in RCS file %s", + c, rcsbuf->filename); + + *revp = ptr; + + do + { + ++ptr; + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, revp, (char **) NULL); + if (ptr == NULL) + error (1, 0, + "unexpected EOF reading revision number in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + c = *ptr; + } + while (isdigit ((unsigned char) c) || c == '.'); + + if (! whitespace (c)) + error (1, 0, "\ +unexpected '\\x%x' reading revision number in RCS file %s", + c, rcsbuf->filename); + + *ptr = '\0'; + + rcsbuf->ptr = ptr + 1; + + return 1; +} + +/* Fill RCSBUF_BUFFER with bytes from the file associated with RCSBUF, + updating PTR and the PTREND field. If KEYP and *KEYP are not NULL, + then *KEYP points into the buffer, and must be adjusted if the + buffer is changed. Likewise for VALP. Returns the new value of + PTR, or NULL on error. */ + +static char * +rcsbuf_fill (rcsbuf, ptr, keyp, valp) + struct rcsbuffer *rcsbuf; + char *ptr; + char **keyp; + char **valp; +{ +#ifdef HAVE_MMAP + return NULL; +#else /* HAVE_MMAP */ + int got; + + if (rcsbuf->ptrend - rcsbuf_buffer + RCSBUF_BUFSIZE > rcsbuf_buffer_size) + { + int poff, peoff, koff, voff; + + poff = ptr - rcsbuf_buffer; + peoff = rcsbuf->ptrend - rcsbuf_buffer; + koff = keyp == NULL ? 0 : *keyp - rcsbuf_buffer; + voff = valp == NULL ? 0 : *valp - rcsbuf_buffer; + + expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, + rcsbuf_buffer_size + RCSBUF_BUFSIZE); + + ptr = rcsbuf_buffer + poff; + rcsbuf->ptrend = rcsbuf_buffer + peoff; + if (keyp != NULL) + *keyp = rcsbuf_buffer + koff; + if (valp != NULL) + *valp = rcsbuf_buffer + voff; + } + + got = fread (rcsbuf->ptrend, 1, RCSBUF_BUFSIZE, rcsbuf->fp); + if (got == 0) + { + if (ferror (rcsbuf->fp)) + error (1, errno, "cannot read %s", rcsbuf->filename); + return NULL; + } + + rcsbuf->ptrend += got; + + return ptr; +#endif /* HAVE_MMAP */ +} + +/* Test whether the last value returned by rcsbuf_getkey is a composite + value or not. */ + +static int +rcsbuf_valcmp (struct rcsbuffer *rcsbuf) +{ + return rcsbuf->at_string && rcsbuf->embedded_at < 0; +} + +/* Copy the value VAL returned by rcsbuf_getkey into a memory buffer, + returning the memory buffer. Polish the value like + rcsbuf_valpolish, q.v. */ + +static char * +rcsbuf_valcopy (struct rcsbuffer *rcsbuf, char *val, int polish, size_t *lenp) +{ + size_t vlen; + int embedded_at; + char *ret; + + if (val == NULL) + { + if (lenp != NULL) + *lenp = 0; + return NULL; + } + + vlen = rcsbuf->vlen; + embedded_at = rcsbuf->embedded_at < 0 ? 0 : rcsbuf->embedded_at; + + ret = xmalloc (vlen - embedded_at + 1); + + if (rcsbuf->at_string ? embedded_at == 0 : ! polish) + { + /* No special action to take. */ + memcpy (ret, val, vlen + 1); + if (lenp != NULL) + *lenp = vlen; + return ret; + } + + rcsbuf_valpolish_internal (rcsbuf, ret, val, lenp); + return ret; +} + +/* Polish the value VAL returned by rcsbuf_getkey. The POLISH + parameter is non-zero if multiple embedded whitespace characters + should be compressed into a single whitespace character. Note that + leading and trailing whitespace was already removed by + rcsbuf_getkey. Within an '@' string, pairs of '@' characters are + compressed into a single '@' character regardless of the value of + POLISH. If LENP is not NULL, set *LENP to the length of the value. */ + +static void +rcsbuf_valpolish (struct rcsbuffer *rcsbuf, char *val, int polish, size_t *lenp) +{ + if (val == NULL) + { + if (lenp != NULL) + *lenp= 0; + return; + } + + if (rcsbuf->at_string ? rcsbuf->embedded_at == 0 : ! polish) + { + /* No special action to take. */ + if (lenp != NULL) + *lenp = rcsbuf->vlen; + return; + } + + rcsbuf_valpolish_internal (rcsbuf, val, val, lenp); +} + +/* Internal polishing routine, called from rcsbuf_valcopy and + rcsbuf_valpolish. */ + +static void +rcsbuf_valpolish_internal (struct rcsbuffer *rcsbuf, char *to, const char *from, size_t *lenp) +{ + size_t len; + + len = rcsbuf->vlen; + + if (! rcsbuf->at_string) + { + char *orig_to; + size_t clen; + + orig_to = to; + + for (clen = len; clen > 0; ++from, --clen) + { + char c; + + c = *from; + if (whitespace (c)) + { + /* Note that we know that clen can not drop to zero + while we have whitespace, because we know there is + no trailing whitespace. */ + while (whitespace (from[1])) + { + ++from; + --clen; + } + c = ' '; + } + *to++ = c; + } + + *to = '\0'; + + if (lenp != NULL) + *lenp = to - orig_to; + } + else + { + const char *orig_from; + char *orig_to; + int embedded_at; + size_t clen; + + orig_from = from; + orig_to = to; + + embedded_at = rcsbuf->embedded_at; + assert (embedded_at > 0); + + if (lenp != NULL) + *lenp = len - embedded_at; + + for (clen = len; clen > 0; ++from, --clen) + { + char c; + + c = *from; + *to++ = c; + if (c == '@') + { + ++from; + + /* Sanity check. + * + * FIXME: I restored this to an abort from an assert based on + * advice from Larry Jones that asserts should not be used to + * confirm the validity of an RCS file... This leaves two + * issues here: 1) I am uncertain that the fact that we will + * only find double '@'s hasn't already been confirmed; and: + * 2) If this is the proper place to spot the error in the RCS + * file, then we should print a much clearer error here for the + * user!!!!!!! + * + * - DRP + */ + if (*from != '@' || clen == 0) + abort (); + + --clen; + + --embedded_at; + if (embedded_at == 0) + { + /* We've found all the embedded '@' characters. + We can just memcpy the rest of the buffer after + this '@' character. */ + if (orig_to != orig_from) + memcpy (to, from + 1, clen - 1); + else + memmove (to, from + 1, clen - 1); + from += clen; + to += clen - 1; + break; + } + } + } + + /* Sanity check. */ + assert (from == orig_from + len + && to == orig_to + (len - rcsbuf->embedded_at)); + + *to = '\0'; + } +} + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + +/* Copy the next word from the value VALP returned by rcsbuf_getkey into a + memory buffer, updating VALP and returning the memory buffer. Return + NULL when there are no more words. */ + +static char * +rcsbuf_valword (rcsbuf, valp) + struct rcsbuffer *rcsbuf; + char **valp; +{ + register const char * const my_spacetab = spacetab; + register char *ptr, *pat; + char c; + +# define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0) + + if (*valp == NULL) + return NULL; + + for (ptr = *valp; my_whitespace (*ptr); ++ptr) ; + if (*ptr == '\0') + { + assert (ptr - *valp == rcsbuf->vlen); + *valp = NULL; + rcsbuf->vlen = 0; + return NULL; + } + + /* PTR now points to the start of a value. Find out whether it is + a num, an id, a string or a colon. */ + c = *ptr; + if (c == ':') + { + rcsbuf->vlen -= ++ptr - *valp; + *valp = ptr; + return xstrdup (":"); + } + + if (c == '@') + { + int embedded_at = 0; + size_t vlen; + + pat = ++ptr; + while ((pat = strchr (pat, '@')) != NULL) + { + if (pat[1] != '@') + break; + ++embedded_at; + pat += 2; + } + + /* Here PAT points to the final '@' in the string. */ + *pat++ = '\0'; + assert (rcsbuf->at_string); + vlen = rcsbuf->vlen - (pat - *valp); + rcsbuf->vlen = pat - ptr - 1; + rcsbuf->embedded_at = embedded_at; + ptr = rcsbuf_valcopy (rcsbuf, ptr, 0, (size_t *) NULL); + *valp = pat; + rcsbuf->vlen = vlen; + if (strchr (pat, '@') == NULL) + rcsbuf->at_string = 0; + else + rcsbuf->embedded_at = -1; + return ptr; + } + + /* *PTR is neither `:', `;' nor `@', so it should be the start of a num + or an id. Make sure it is not another special character. */ + if (c == '$' || c == '.' || c == ',') + { + error (1, 0, "invalid special character in RCS field in %s", + rcsbuf->filename); + } + + pat = ptr; + while (1) + { + /* Legitimate ID characters are digits, dots and any `graphic + printing character that is not a special.' This test ought + to do the trick. */ + c = *++pat; + if (!isprint ((unsigned char) c) || + c == ';' || c == '$' || c == ',' || c == '@' || c == ':') + break; + } + + /* PAT points to the last non-id character in this word, and C is + the character in its memory cell. Check to make sure that it + is a legitimate word delimiter -- whitespace or end. */ + if (c != '\0' && !my_whitespace (c)) + error (1, 0, "invalid special character in RCS field in %s", + rcsbuf->filename); + + *pat = '\0'; + rcsbuf->vlen -= pat - *valp; + *valp = pat; + return xstrdup (ptr); + +# undef my_whitespace +} + +#endif + +/* Return the current position of an rcsbuf. */ + +static off_t +rcsbuf_ftello (struct rcsbuffer *rcsbuf) +{ + return rcsbuf->pos + rcsbuf->ptr - rcsbuf_buffer; +} + +/* Return a pointer to any data buffered for RCSBUF, along with the + length. */ + +static void +rcsbuf_get_buffered (struct rcsbuffer *rcsbuf, char **datap, size_t *lenp) +{ + *datap = rcsbuf->ptr; + *lenp = rcsbuf->ptrend - rcsbuf->ptr; +} + +/* CVS optimizes by quickly reading some header information from a + file. If it decides it needs to do more with the file, it reopens + it. We speed that up here by maintaining a cache of a single open + file, to save the time it takes to reopen the file in the common + case. */ + +static RCSNode *cached_rcs; +static struct rcsbuffer cached_rcsbuf; + +/* Cache RCS and RCSBUF. This takes responsibility for closing + RCSBUF->FP. */ + +static void +rcsbuf_cache (RCSNode *rcs, struct rcsbuffer *rcsbuf) +{ + if (cached_rcs != NULL) + rcsbuf_cache_close (); + cached_rcs = rcs; + ++rcs->refcount; + cached_rcsbuf = *rcsbuf; +} + +/* If there is anything in the cache, close it. */ + +static void +rcsbuf_cache_close (void) +{ + if (cached_rcs != NULL) + { + rcsbuf_close (&cached_rcsbuf); + if (fclose (cached_rcsbuf.fp) != 0) + error (0, errno, "cannot close %s", cached_rcsbuf.filename); + freercsnode (&cached_rcs); + cached_rcs = NULL; + } +} + +/* Open an rcsbuffer for RCS, getting it from the cache if possible. + Set *FPP to the file, and *RCSBUFP to the rcsbuf. The file should + be put at position POS. */ + +static void +rcsbuf_cache_open (RCSNode *rcs, off_t pos, FILE **pfp, + struct rcsbuffer *prcsbuf) +{ +#ifndef HAVE_MMAP + if (cached_rcs == rcs) + { + if (rcsbuf_ftello (&cached_rcsbuf) != pos) + { + if (fseeko (cached_rcsbuf.fp, pos, SEEK_SET) != 0) + error (1, 0, "cannot fseeko RCS file %s", + cached_rcsbuf.filename); + cached_rcsbuf.ptr = rcsbuf_buffer; + cached_rcsbuf.ptrend = rcsbuf_buffer; + cached_rcsbuf.pos = pos; + } + *pfp = cached_rcsbuf.fp; + + /* When RCS_parse opens a file using fopen_case, it frees the + filename which we cached in CACHED_RCSBUF and stores a new + file name in RCS->PATH. We avoid problems here by always + copying the filename over. FIXME: This is hackish. */ + cached_rcsbuf.filename = rcs->path; + + *prcsbuf = cached_rcsbuf; + + cached_rcs = NULL; + + /* Removing RCS from the cache removes a reference to it. */ + --rcs->refcount; + if (rcs->refcount <= 0) + error (1, 0, "rcsbuf_cache_open: internal error"); + } + else + { +#endif /* ifndef HAVE_MMAP */ + /* FIXME: If these routines can be rewritten to not write to the + * rcs file buffer, there would be a considerably larger memory savings + * from using mmap since the shared file would never need be copied to + * process memory. + * + * If this happens, cached mmapped buffers would be usable, but don't + * forget to make sure rcs->pos < pos here... + */ + if (cached_rcs != NULL) + rcsbuf_cache_close (); + + *pfp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); + if (*pfp == NULL) + error (1, 0, "unable to reopen `%s'", rcs->path); +#ifndef HAVE_MMAP + if (pos != 0) + { + if (fseeko (*pfp, pos, SEEK_SET) != 0) + error (1, 0, "cannot fseeko RCS file %s", rcs->path); + } +#endif /* ifndef HAVE_MMAP */ + rcsbuf_open (prcsbuf, *pfp, rcs->path, pos); +#ifndef HAVE_MMAP + } +#endif /* ifndef HAVE_MMAP */ +} + + +/* + * process the symbols list of the rcs file + */ +static void +do_symbols (List *list, char *val) +{ + Node *p; + char *cp = val; + char *tag, *rev; + + for (;;) + { + /* skip leading whitespace */ + while (whitespace (*cp)) + cp++; + + /* if we got to the end, we are done */ + if (*cp == '\0') + break; + + /* split it up into tag and rev */ + tag = cp; + cp = strchr (cp, ':'); + *cp++ = '\0'; + rev = cp; + while (!whitespace (*cp) && *cp != '\0') + cp++; + if (*cp != '\0') + *cp++ = '\0'; + + /* make a new node and add it to the list */ + p = getnode (); + p->key = xstrdup (tag); + p->data = xstrdup (rev); + (void) addnode (list, p); + } +} + +/* + * process the locks list of the rcs file + * Like do_symbols, but hash entries are keyed backwards: i.e. + * an entry like `user:rev' is keyed on REV rather than on USER. + */ +static void +do_locks (List *list, char *val) +{ + Node *p; + char *cp = val; + char *user, *rev; + + for (;;) + { + /* skip leading whitespace */ + while (whitespace (*cp)) + cp++; + + /* if we got to the end, we are done */ + if (*cp == '\0') + break; + + /* split it up into user and rev */ + user = cp; + cp = strchr (cp, ':'); + *cp++ = '\0'; + rev = cp; + while (!whitespace (*cp) && *cp != '\0') + cp++; + if (*cp != '\0') + *cp++ = '\0'; + + /* make a new node and add it to the list */ + p = getnode (); + p->key = xstrdup (rev); + p->data = xstrdup (user); + (void) addnode (list, p); + } +} + +/* + * process the branches list of a revision delta + */ +static void +do_branches (List *list, char *val) +{ + Node *p; + char *cp = val; + char *branch; + + for (;;) + { + /* skip leading whitespace */ + while (whitespace (*cp)) + cp++; + + /* if we got to the end, we are done */ + if (*cp == '\0') + break; + + /* find the end of this branch */ + branch = cp; + while (!whitespace (*cp) && *cp != '\0') + cp++; + if (*cp != '\0') + *cp++ = '\0'; + + /* make a new node and add it to the list */ + p = getnode (); + p->key = xstrdup (branch); + (void) addnode (list, p); + } +} + + + +/* + * Version Number + * + * Returns the requested version number of the RCS file, satisfying tags and/or + * dates, and walking branches, if necessary. + * + * The result is returned; null-string if error. + */ +char * +RCS_getversion (RCSNode *rcs, const char *tag, const char *date, + int force_tag_match, int *simple_tag) +{ + if (simple_tag != NULL) + *simple_tag = 0; + + /* make sure we have something to look at... */ + assert (rcs != NULL); + + if (tag && date) + { + char *branch, *rev; + + if (! RCS_nodeisbranch (rcs, tag)) + { + /* We can't get a particular date if the tag is not a + branch. */ + return NULL; + } + + /* Work out the branch. */ + if (! isdigit ((unsigned char) tag[0])) + branch = RCS_whatbranch (rcs, tag); + else + branch = xstrdup (tag); + + /* Fetch the revision of branch as of date. */ + rev = RCS_getdatebranch (rcs, date, branch); + free (branch); + return (rev); + } + else if (tag) + return RCS_gettag (rcs, tag, force_tag_match, simple_tag); + else if (date) + return RCS_getdate (rcs, date, force_tag_match); + else + return RCS_head (rcs); + +} + + + +/* + * Get existing revision number corresponding to tag or revision. + * Similar to RCS_gettag but less interpretation imposed. + * For example: + * -- If tag designates a magic branch, RCS_tag2rev + * returns the magic branch number. + * -- If tag is a branch tag, returns the branch number, not + * the revision of the head of the branch. + * If tag or revision is not valid or does not exist in file, + * return NULL. + */ +char * +RCS_tag2rev (RCSNode *rcs, char *tag) +{ + char *rev, *pa, *pb; + int i; + + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + /* If a valid revision, try to look it up */ + if ( RCS_valid_rev (tag) ) + { + /* Make a copy so we can scribble on it */ + rev = xstrdup (tag); + + /* If revision exists, return the copy */ + if (RCS_exist_rev (rcs, tag)) + return rev; + + /* Nope, none such. If tag is not a branch we're done. */ + i = numdots (rev); + if ((i & 1) == 1 ) + { + pa = strrchr (rev, '.'); + if (i == 1 || *(pa-1) != RCS_MAGIC_BRANCH || *(pa-2) != '.') + { + free (rev); + error (1, 0, "revision `%s' does not exist", tag); + } + } + + /* Try for a real (that is, exists in the RCS deltas) branch + (RCS_exist_rev just checks for real revisions and revisions + which have tags pointing to them). */ + pa = RCS_getbranch (rcs, rev, 1); + if (pa != NULL) + { + free (pa); + return rev; + } + + /* Tag is branch, but does not exist, try corresponding + * magic branch tag. + * + * FIXME: assumes all magic branches are of + * form "n.n.n ... .0.n". I'll fix if somebody can + * send me a method to get a magic branch tag with + * the 0 in some other position -- + */ + pa = strrchr (rev, '.'); + pb = xmalloc (strlen (rev) + 3); + *pa++ = 0; + (void) sprintf (pb, "%s.%d.%s", rev, RCS_MAGIC_BRANCH, pa); + free (rev); + rev = pb; + if (RCS_exist_rev (rcs, rev)) + return rev; + error (1, 0, "revision `%s' does not exist", tag); + } + + + RCS_check_tag (tag); /* exit if not a valid tag */ + + /* If tag is "HEAD", special case to get head RCS revision */ + if (tag && STREQ (tag, TAG_HEAD)) + return (RCS_head (rcs)); + + /* If valid tag let translate_symtag say yea or nay. */ + rev = translate_symtag (rcs, tag); + + if (rev) + return rev; + + /* Trust the caller to print warnings. */ + return NULL; +} + +/* + * Find the revision for a specific tag. + * If force_tag_match is set, return NULL if an exact match is not + * possible otherwise return RCS_head (). We are careful to look for + * and handle "magic" revisions specially. + * + * If the matched tag is a branch tag, find the head of the branch. + * + * Returns pointer to newly malloc'd string, or NULL. + */ +char * +RCS_gettag (RCSNode *rcs, const char *symtag, int force_tag_match, + int *simple_tag) +{ + char *tag; + + if (simple_tag != NULL) + *simple_tag = 0; + + /* make sure we have something to look at... */ + assert (rcs != NULL); + + /* XXX this is probably not necessary, --jtc */ + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + /* If symtag is "HEAD", special case to get head RCS revision */ + if (symtag && STREQ (symtag, TAG_HEAD)) +#if 0 /* This #if 0 is only in the Cygnus code. Why? Death support? */ + if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC)) + return ((char *) NULL); /* head request for removed file */ + else +#endif + return RCS_head (rcs); + + if (!isdigit ((unsigned char) symtag[0])) + { + char *version; + + /* If we got a symbolic tag, resolve it to a numeric */ + version = translate_symtag (rcs, symtag); + if (version != NULL) + { + int dots; + char *magic, *branch, *cp; + + tag = version; + + /* + * If this is a magic revision, we turn it into either its + * physical branch equivalent (if one exists) or into + * its base revision, which we assume exists. + */ + dots = numdots (tag); + if (dots > 2 && (dots & 1) != 0) + { + branch = strrchr (tag, '.'); + cp = branch++ - 1; + while (*cp != '.') + cp--; + + /* see if we have .magic-branch. (".0.") */ + magic = xmalloc (strlen (tag) + 1); + (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH); + if (strncmp (magic, cp, strlen (magic)) == 0) + { + /* it's magic. See if the branch exists */ + *cp = '\0'; /* turn it into a revision */ + (void) sprintf (magic, "%s.%s", tag, branch); + branch = RCS_getbranch (rcs, magic, 1); + free (magic); + if (branch != NULL) + { + free (tag); + return branch; + } + return tag; + } + free (magic); + } + } + else + { + /* The tag wasn't there, so return the head or NULL */ + if (force_tag_match) + return NULL; + else + return RCS_head (rcs); + } + } + else + tag = xstrdup (symtag); + + /* tag is always allocated and numeric now. */ + + /* + * numeric tag processing: + * 1) revision number - just return it + * 2) branch number - find head of branch + */ + + /* strip trailing dots */ + while (tag[strlen (tag) - 1] == '.') + tag[strlen (tag) - 1] = '\0'; + + if ((numdots (tag) & 1) == 0) + { + char *branch; + + /* we have a branch tag, so we need to walk the branch */ + branch = RCS_getbranch (rcs, tag, force_tag_match); + free (tag); + return branch; + } + else + { + Node *p; + + /* we have a revision tag, so make sure it exists */ + p = findnode (rcs->versions, tag); + if (p != NULL) + { + /* We have found a numeric revision for the revision tag. + To support expanding the RCS keyword Name, if + SIMPLE_TAG is not NULL, tell the the caller that this + is a simple tag which co will recognize. FIXME: Are + there other cases in which we should set this? In + particular, what if we expand RCS keywords internally + without calling co? */ + if (simple_tag != NULL) + *simple_tag = 1; + return tag; + } + else + { + /* The revision wasn't there, so return the head or NULL */ + free (tag); + if (force_tag_match) + return NULL; + else + return RCS_head (rcs); + } + } +} + +/* + * Return a "magic" revision as a virtual branch off of REV for the RCS file. + * A "magic" revision is one which is unique in the RCS file. By unique, I + * mean we return a revision which: + * - has a branch of 0 (see rcs.h RCS_MAGIC_BRANCH) + * - has a revision component which is not an existing branch off REV + * - has a revision component which is not an existing magic revision + * - is an even-numbered revision, to avoid conflicts with vendor branches + * The first point is what makes it "magic". + * + * As an example, if we pass in 1.37 as REV, we will look for an existing + * branch called 1.37.2. If it did not exist, we would look for an + * existing symbolic tag with a numeric part equal to 1.37.0.2. If that + * didn't exist, then we know that the 1.37.2 branch can be reserved by + * creating a symbolic tag with 1.37.0.2 as the numeric part. + * + * This allows us to fork development with very little overhead -- just a + * symbolic tag is used in the RCS file. When a commit is done, a physical + * branch is dynamically created to hold the new revision. + * + * Note: We assume that REV is an RCS revision and not a branch number. + */ +static char *check_rev; +char * +RCS_magicrev (RCSNode *rcs, char *rev) +{ + int rev_num; + char *xrev, *test_branch, *local_branch_num; + + xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */ + check_rev = xrev; + + local_branch_num = getenv("CVS_LOCAL_BRANCH_NUM"); + if (local_branch_num) + { + rev_num = atoi(local_branch_num); + if (rev_num < 2) + rev_num = 2; + else + rev_num &= ~1; + } + else + rev_num = 2; + + /* only look at even numbered branches */ + for ( ; ; rev_num += 2) + { + /* see if the physical branch exists */ + (void) sprintf (xrev, "%s.%d", rev, rev_num); + test_branch = RCS_getbranch (rcs, xrev, 1); + if (test_branch != NULL) /* it did, so keep looking */ + { + free (test_branch); + continue; + } + + /* now, create a "magic" revision */ + (void) sprintf (xrev, "%s.%d.%d", rev, RCS_MAGIC_BRANCH, rev_num); + + /* walk the symbols list to see if a magic one already exists */ + if (walklist (RCS_symbols(rcs), checkmagic_proc, NULL) != 0) + continue; + + /* we found a free magic branch. Claim it as ours */ + return (xrev); + } +} + +/* + * walklist proc to look for a match in the symbols list. + * Returns 0 if the symbol does not match, 1 if it does. + */ +static int +checkmagic_proc (Node *p, void *closure) +{ + if (STREQ (check_rev, p->data)) + return (1); + else + return (0); +} + +/* + * Given an RCSNode, returns non-zero if the specified revision number + * or symbolic tag resolves to a "branch" within the rcs file. + * + * FIXME: this is the same as RCS_nodeisbranch except for the special + * case for handling a null rcsnode. + */ +int +RCS_isbranch (RCSNode *rcs, const char *rev) +{ + /* numeric revisions are easy -- even number of dots is a branch */ + if (isdigit ((unsigned char) *rev)) + return ((numdots (rev) & 1) == 0); + + /* assume a revision if you can't find the RCS info */ + if (rcs == NULL) + return (0); + + /* now, look for a match in the symbols list */ + return (RCS_nodeisbranch (rcs, rev)); +} + +/* + * Given an RCSNode, returns non-zero if the specified revision number + * or symbolic tag resolves to a "branch" within the rcs file. We do + * take into account any magic branches as well. + */ +int +RCS_nodeisbranch (RCSNode *rcs, const char *rev) +{ + int dots; + char *version; + + assert (rcs != NULL); + + /* numeric revisions are easy -- even number of dots is a branch */ + if (isdigit ((unsigned char) *rev)) + return ((numdots (rev) & 1) == 0); + + version = translate_symtag (rcs, rev); + if (version == NULL) + return (0); + dots = numdots (version); + if ((dots & 1) == 0) + { + free (version); + return (1); + } + + /* got a symbolic tag match, but it's not a branch; see if it's magic */ + if (dots > 2) + { + char *magic; + char *branch = strrchr (version, '.'); + char *cp = branch - 1; + while (*cp != '.') + cp--; + + /* see if we have .magic-branch. (".0.") */ + magic = xmalloc (strlen (version) + 1); + (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH); + if (strncmp (magic, cp, strlen (magic)) == 0) + { + free (magic); + free (version); + return (1); + } + free (magic); + } + free (version); + return (0); +} + +/* + * Returns a pointer to malloc'ed memory which contains the branch + * for the specified *symbolic* tag. Magic branches are handled correctly. + */ +char * +RCS_whatbranch (RCSNode *rcs, const char *rev) +{ + char *version; + int dots; + + /* assume no branch if you can't find the RCS info */ + if (rcs == NULL) + return ((char *) NULL); + + /* now, look for a match in the symbols list */ + version = translate_symtag (rcs, rev); + if (version == NULL) + return ((char *) NULL); + dots = numdots (version); + if ((dots & 1) == 0) + return (version); + + /* got a symbolic tag match, but it's not a branch; see if it's magic */ + if (dots > 2) + { + char *magic; + char *branch = strrchr (version, '.'); + char *cp = branch++ - 1; + while (*cp != '.') + cp--; + + /* see if we have .magic-branch. (".0.") */ + magic = xmalloc (strlen (version) + 1); + (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH); + if (strncmp (magic, cp, strlen (magic)) == 0) + { + /* yep. it's magic. now, construct the real branch */ + *cp = '\0'; /* turn it into a revision */ + (void) sprintf (magic, "%s.%s", version, branch); + free (version); + return (magic); + } + free (magic); + } + free (version); + return ((char *) NULL); +} + +/* + * Get the head of the specified branch. If the branch does not exist, + * return NULL or RCS_head depending on force_tag_match. + * Returns NULL or a newly malloc'd string. + */ +char * +RCS_getbranch (RCSNode *rcs, const char *tag, int force_tag_match) +{ + Node *p, *head; + RCSVers *vn; + char *xtag; + char *nextvers; + char *cp; + + /* make sure we have something to look at... */ + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + /* find out if the tag contains a dot, or is on the trunk */ + cp = strrchr (tag, '.'); + + /* trunk processing is the special case */ + if (cp == NULL) + { + xtag = xmalloc (strlen (tag) + 1 + 1); /* +1 for an extra . */ + (void) strcpy (xtag, tag); + (void) strcat (xtag, "."); + for (cp = rcs->head; cp != NULL;) + { + if (strncmp (xtag, cp, strlen (xtag)) == 0) + break; + p = findnode (rcs->versions, cp); + if (p == NULL) + { + free (xtag); + if (force_tag_match) + return (NULL); + else + return (RCS_head (rcs)); + } + vn = p->data; + cp = vn->next; + } + free (xtag); + if (cp == NULL) + { + if (force_tag_match) + return (NULL); + else + return (RCS_head (rcs)); + } + return (xstrdup (cp)); + } + + /* if it had a `.', terminate the string so we have the base revision */ + *cp = '\0'; + + /* look up the revision this branch is based on */ + p = findnode (rcs->versions, tag); + + /* put the . back so we have the branch again */ + *cp = '.'; + + if (p == NULL) + { + /* if the base revision didn't exist, return head or NULL */ + if (force_tag_match) + return (NULL); + else + return (RCS_head (rcs)); + } + + /* find the first element of the branch we are looking for */ + vn = p->data; + if (vn->branches == NULL) + return (NULL); + xtag = xmalloc (strlen (tag) + 1 + 1); /* 1 for the extra '.' */ + (void) strcpy (xtag, tag); + (void) strcat (xtag, "."); + head = vn->branches->list; + for (p = head->next; p != head; p = p->next) + if (strncmp (p->key, xtag, strlen (xtag)) == 0) + break; + free (xtag); + + if (p == head) + { + /* we didn't find a match so return head or NULL */ + if (force_tag_match) + return (NULL); + else + return (RCS_head (rcs)); + } + + /* now walk the next pointers of the branch */ + nextvers = p->key; + do + { + p = findnode (rcs->versions, nextvers); + if (p == NULL) + { + /* a link in the chain is missing - return head or NULL */ + if (force_tag_match) + return (NULL); + else + return (RCS_head (rcs)); + } + vn = p->data; + nextvers = vn->next; + } while (nextvers != NULL); + + /* we have the version in our hand, so go for it */ + return (xstrdup (vn->version)); +} + +/* Returns the head of the branch which REV is on. REV can be a + branch tag or non-branch tag; symbolic or numeric. + + Returns a newly malloc'd string. Returns NULL if a symbolic name + isn't found. */ + +char * +RCS_branch_head (RCSNode *rcs, char *rev) +{ + char *num; + char *br; + char *retval; + + assert (rcs != NULL); + + if (RCS_nodeisbranch (rcs, rev)) + return RCS_getbranch (rcs, rev, 1); + + if (isdigit ((unsigned char) *rev)) + num = xstrdup (rev); + else + { + num = translate_symtag (rcs, rev); + if (num == NULL) + return NULL; + } + br = truncate_revnum (num); + retval = RCS_getbranch (rcs, br, 1); + free (br); + free (num); + return retval; +} + +/* Get the branch point for a particular branch, that is the first + revision on that branch. For example, RCS_getbranchpoint (rcs, + "1.3.2") will normally return "1.3.2.1". TARGET may be either a + branch number or a revision number; if a revnum, find the + branchpoint of the branch to which TARGET belongs. + + Return RCS_head if TARGET is on the trunk or if the root node could + not be found (this is sort of backwards from our behavior on a branch; + the rationale is that the return value is a revision from which you + can start walking the next fields and end up at TARGET). + Return NULL on error. */ + +static char * +RCS_getbranchpoint (RCSNode *rcs, char *target) +{ + char *branch, *bp; + Node *vp; + RCSVers *rev; + int dots, isrevnum, brlen; + + dots = numdots (target); + isrevnum = dots & 1; + + if (dots == 1) + /* TARGET is a trunk revision; return rcs->head. */ + return (RCS_head (rcs)); + + /* Get the revision number of the node at which TARGET's branch is + rooted. If TARGET is a branch number, lop off the last field; + if it's a revision number, lop off the last *two* fields. */ + branch = xstrdup (target); + bp = strrchr (branch, '.'); + if (bp == NULL) + error (1, 0, "%s: confused revision number %s", + rcs->path, target); + if (isrevnum) + while (*--bp != '.') + ; + *bp = '\0'; + + vp = findnode (rcs->versions, branch); + if (vp == NULL) + { + error (0, 0, "%s: can't find branch point %s", rcs->path, target); + return NULL; + } + rev = vp->data; + + *bp++ = '.'; + while (*bp && *bp != '.') + ++bp; + brlen = bp - branch; + + vp = rev->branches->list->next; + while (vp != rev->branches->list) + { + /* BRANCH may be a genuine branch number, e.g. `1.1.3', or + maybe a full revision number, e.g. `1.1.3.6'. We have + found our branch point if the first BRANCHLEN characters + of the revision number match, *and* if the following + character is a dot. */ + if (strncmp (vp->key, branch, brlen) == 0 && vp->key[brlen] == '.') + break; + vp = vp->next; + } + + free (branch); + if (vp == rev->branches->list) + { + error (0, 0, "%s: can't find branch point %s", rcs->path, target); + return NULL; + } + else + return (xstrdup (vp->key)); +} + +/* + * Get the head of the RCS file. If branch is set, this is the head of the + * branch, otherwise the real head. + * Returns NULL or a newly malloc'd string. + */ +char * +RCS_head (RCSNode *rcs) +{ + /* make sure we have something to look at... */ + assert (rcs != NULL); + + /* + * NOTE: we call getbranch with force_tag_match set to avoid any + * possibility of recursion + */ + if (rcs->branch) + return (RCS_getbranch (rcs, rcs->branch, 1)); + else + return (xstrdup (rcs->head)); +} + +/* + * Get the most recent revision, based on the supplied date, but use some + * funky stuff and follow the vendor branch maybe + */ +char * +RCS_getdate (RCSNode *rcs, const char *date, int force_tag_match) +{ + char *cur_rev = NULL; + char *retval = NULL; + Node *p; + RCSVers *vers = NULL; + + /* make sure we have something to look at... */ + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + /* if the head is on a branch, try the branch first */ + if (rcs->branch != NULL) + { + retval = RCS_getdatebranch (rcs, date, rcs->branch); + if (retval != NULL) + return (retval); + } + + /* otherwise if we have a trunk, try it */ + if (rcs->head) + { + p = findnode (rcs->versions, rcs->head); + if (p == NULL) + { + error (0, 0, "%s: head revision %s doesn't exist", rcs->path, + rcs->head); + } + while (p != NULL) + { + /* if the date of this one is before date, take it */ + vers = p->data; + if (RCS_datecmp (vers->date, date) <= 0) + { + cur_rev = vers->version; + break; + } + + /* if there is a next version, find the node */ + if (vers->next != NULL) + p = findnode (rcs->versions, vers->next); + else + p = (Node *) NULL; + } + } + else + error (0, 0, "%s: no head revision", rcs->path); + + /* + * at this point, either we have the revision we want, or we have the + * first revision on the trunk (1.1?) in our hands, or we've come up + * completely empty + */ + + /* if we found what we're looking for, and it's not 1.1 return it */ + if (cur_rev != NULL) + { + if (! STREQ (cur_rev, "1.1")) + return (xstrdup (cur_rev)); + + /* This is 1.1; if the date of 1.1 is not the same as that for the + 1.1.1.1 version, then return 1.1. This happens when the first + version of a file is created by a regular cvs add and commit, + and there is a subsequent cvs import of the same file. */ + p = findnode (rcs->versions, "1.1.1.1"); + if (p) + { + char *date_1_1 = vers->date; + + vers = p->data; + if (RCS_datecmp (vers->date, date_1_1) != 0) + return xstrdup ("1.1"); + } + } + + /* look on the vendor branch */ + retval = RCS_getdatebranch (rcs, date, CVSBRANCH); + + /* + * if we found a match, return it; otherwise, we return the first + * revision on the trunk or NULL depending on force_tag_match and the + * date of the first rev + */ + if (retval != NULL) + return (retval); + + if (!force_tag_match || + (vers != NULL && RCS_datecmp (vers->date, date) <= 0)) + return xstrdup (vers->version); + else + return NULL; +} + + + +/* + * Look up the last element on a branch that was put in before or on + * the specified date and time (return the rev or NULL) + */ +static char * +RCS_getdatebranch (RCSNode *rcs, const char *date, const char *branch) +{ + char *cur_rev = NULL; + char *cp; + char *xbranch, *xrev; + Node *p; + RCSVers *vers; + + /* look up the first revision on the branch */ + xrev = xstrdup (branch); + cp = strrchr (xrev, '.'); + if (cp == NULL) + { + free (xrev); + return (NULL); + } + *cp = '\0'; /* turn it into a revision */ + + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + p = findnode (rcs->versions, xrev); + free (xrev); + if (p == NULL) + return (NULL); + vers = p->data; + + /* Tentatively use this revision, if it is early enough. */ + if (RCS_datecmp (vers->date, date) <= 0) + cur_rev = vers->version; + + /* If no branches list, return now. This is what happens if the branch + is a (magic) branch with no revisions yet. */ + if (vers->branches == NULL) + return xstrdup (cur_rev); + + /* walk the branches list looking for the branch number */ + xbranch = xmalloc (strlen (branch) + 1 + 1); /* +1 for the extra dot */ + (void) strcpy (xbranch, branch); + (void) strcat (xbranch, "."); + for (p = vers->branches->list->next; p != vers->branches->list; p = p->next) + if (strncmp (p->key, xbranch, strlen (xbranch)) == 0) + break; + free (xbranch); + if (p == vers->branches->list) + { + /* This is what happens if the branch is a (magic) branch with + no revisions yet. Similar to the case where vers->branches == + NULL, except here there was a another branch off the same + branchpoint. */ + return xstrdup (cur_rev); + } + + p = findnode (rcs->versions, p->key); + + /* walk the next pointers until you find the end, or the date is too late */ + while (p != NULL) + { + vers = p->data; + if (RCS_datecmp (vers->date, date) <= 0) + cur_rev = vers->version; + else + break; + + /* if there is a next version, find the node */ + if (vers->next != NULL) + p = findnode (rcs->versions, vers->next); + else + p = (Node *) NULL; + } + + /* Return whatever we found, which may be NULL. */ + return xstrdup (cur_rev); +} + + + +/* + * Compare two dates in RCS format. Beware the change in format on January 1, + * 2000, when years go from 2-digit to full format. + */ +int +RCS_datecmp (const char *date1, const char *date2) +{ + int length_diff = strlen (date1) - strlen (date2); + + return length_diff ? length_diff : strcmp (date1, date2); +} + + + +/* Look up revision REV in RCS and return the date specified for the + revision minus FUDGE seconds (FUDGE will generally be one, so that the + logically previous revision will be found later, or zero, if we want + the exact date). + + The return value is the date being returned as a time_t, or (time_t)-1 + on error (previously was documented as zero on error; I haven't checked + the callers to make sure that they really check for (time_t)-1, but + the latter is what this function really returns). If DATE is non-NULL, + then it must point to MAXDATELEN characters, and we store the same + return value there in DATEFORM format. */ +time_t +RCS_getrevtime (RCSNode *rcs, const char *rev, char *date, int fudge) +{ + char tdate[MAXDATELEN]; + struct tm xtm, *ftm; + time_t revdate = 0; + Node *p; + RCSVers *vers; + + /* make sure we have something to look at... */ + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + /* look up the revision */ + p = findnode (rcs->versions, rev); + if (p == NULL) + return (-1); + vers = p->data; + + /* split up the date */ + if (sscanf (vers->date, SDATEFORM, &xtm.tm_year, &xtm.tm_mon, + &xtm.tm_mday, &xtm.tm_hour, &xtm.tm_min, &xtm.tm_sec) != 6) + error (1, 0, "%s: invalid date for revision %s (%s)", rcs->path, + rev, vers->date); + + /* If the year is from 1900 to 1999, RCS files contain only two + digits, and sscanf gives us a year from 0-99. If the year is + 2000+, RCS files contain all four digits and we subtract 1900, + because the tm_year field should contain years since 1900. */ + + if (xtm.tm_year >= 100 && xtm.tm_year < 2000) + error (0, 0, "%s: non-standard date format for revision %s (%s)", + rcs->path, rev, vers->date); + if (xtm.tm_year >= 1900) + xtm.tm_year -= 1900; + + /* put the date in a form getdate can grok */ + (void) sprintf (tdate, "%d/%d/%d GMT %d:%d:%d", xtm.tm_mon, + xtm.tm_mday, xtm.tm_year + 1900, xtm.tm_hour, + xtm.tm_min, xtm.tm_sec); + + /* turn it into seconds since the epoch */ + revdate = get_date (tdate, (struct timeb *) NULL); + if (revdate != (time_t) -1) + { + revdate -= fudge; /* remove "fudge" seconds */ + if (date) + { + /* put an appropriate string into ``date'' if we were given one */ + ftm = gmtime (&revdate); + (void) sprintf (date, DATEFORM, + ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900), + ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour, + ftm->tm_min, ftm->tm_sec); + } + } + return revdate; +} + +List * +RCS_getlocks (RCSNode *rcs) +{ + assert(rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + if (rcs->locks_data) { + rcs->locks = getlist (); + do_locks (rcs->locks, rcs->locks_data); + free(rcs->locks_data); + rcs->locks_data = NULL; + } + + return rcs->locks; +} + +List * +RCS_symbols(RCSNode *rcs) +{ + assert(rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + if (rcs->symbols_data) { + rcs->symbols = getlist (); + do_symbols (rcs->symbols, rcs->symbols_data); + free(rcs->symbols_data); + rcs->symbols_data = NULL; + } + + return rcs->symbols; +} + +/* + * Return the version associated with a particular symbolic tag. + * Returns NULL or a newly malloc'd string. + */ +static char * +translate_symtag (RCSNode *rcs, const char *tag) +{ + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + if (rcs->symbols != NULL) + { + Node *p; + + /* The symbols have already been converted into a list. */ + p = findnode (rcs->symbols, tag); + if (p == NULL) + return NULL; + + return xstrdup (p->data); + } + + if (rcs->symbols_data != NULL) + { + size_t len; + char *cp; + + /* Look through the RCS symbols information. This is like + do_symbols, but we don't add the information to a list. In + most cases, we will only be called once for this file, so + generating the list is unnecessary overhead. */ + + len = strlen (tag); + cp = rcs->symbols_data; + while ((cp = strchr (cp, tag[0])) != NULL) + { + if ((cp == rcs->symbols_data || whitespace (cp[-1])) + && strncmp (cp, tag, len) == 0 + && cp[len] == ':') + { + char *v, *r; + + /* We found the tag. Return the version number. */ + + cp += len + 1; + v = cp; + while (! whitespace (*cp) && *cp != '\0') + ++cp; + r = xmalloc (cp - v + 1); + strncpy (r, v, cp - v); + r[cp - v] = '\0'; + return r; + } + + while (! whitespace (*cp) && *cp != '\0') + ++cp; + if (*cp == '\0') + break; + } + } + + return NULL; +} + +/* + * The argument ARG is the getopt remainder of the -k option specified on the + * command line. This function returns malloc'ed space that can be used + * directly in calls to RCS V5, with the -k flag munged correctly. + */ +char * +RCS_check_kflag (const char *arg) +{ + static const char *const keyword_usage[] = + { + "%s %s: invalid RCS keyword expansion mode\n", + "Valid expansion modes include:\n", + " -kkv\tGenerate keywords using the default form.\n", + " -kkvl\tLike -kkv, except locker's name inserted.\n", + " -kk\tGenerate only keyword names in keyword strings.\n", + " -kv\tGenerate only keyword values in keyword strings.\n", + " -ko\tGenerate the old keyword string (no changes from checked in file).\n", + " -kb\tGenerate binary file unmodified (merges not allowed) (RCS 5.7).\n", + "(Specify the --help global option for a list of other help options)\n", + NULL, + }; + /* Big enough to hold any of the strings from kflags. */ + char karg[10]; + char const *const *cpp = NULL; + + if (arg) + { + for (cpp = kflags; *cpp != NULL; cpp++) + { + if (STREQ (arg, *cpp)) + break; + } + } + + if (arg == NULL || *cpp == NULL) + { + usage (keyword_usage); + } + + (void) sprintf (karg, "-k%s", *cpp); + return (xstrdup (karg)); +} + +/* + * Do some consistency checks on the symbolic tag... These should equate + * pretty close to what RCS checks, though I don't know for certain. + */ +void +RCS_check_tag (const char *tag) +{ + char *invalid = "$,.:;@"; /* invalid RCS tag characters */ + const char *cp; + + /* + * The first character must be an alphabetic letter. The remaining + * characters cannot be non-visible graphic characters, and must not be + * in the set of "invalid" RCS identifier characters. + */ + if (isalpha ((unsigned char) *tag)) + { + for (cp = tag; *cp; cp++) + { + if (!isgraph ((unsigned char) *cp)) + error (1, 0, "tag `%s' has non-visible graphic characters", + tag); + if (strchr (invalid, *cp)) + error (1, 0, "tag `%s' must not contain the characters `%s'", + tag, invalid); + } + } + else + error (1, 0, "tag `%s' must start with a letter", tag); +} + +/* + * TRUE if argument has valid syntax for an RCS revision or + * branch number. All characters must be digits or dots, first + * and last characters must be digits, and no two consecutive + * characters may be dots. + * + * Intended for classifying things, so this function doesn't + * call error. + */ +int +RCS_valid_rev (char *rev) +{ + char last, c; + last = *rev++; + if (!isdigit ((unsigned char) last)) + return 0; + while ((c = *rev++)) /* Extra parens placate -Wall gcc option */ + { + if (c == '.') + { + if (last == '.') + return 0; + continue; + } + last = c; + if (!isdigit ((unsigned char) c)) + return 0; + } + if (!isdigit ((unsigned char) last)) + return 0; + return 1; +} + +/* + * Return true if RCS revision with TAG is a dead revision. + */ +int +RCS_isdead (RCSNode *rcs, const char *tag) +{ + Node *p; + RCSVers *version; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + p = findnode (rcs->versions, tag); + if (p == NULL) + return (0); + + version = p->data; + return (version->dead); +} + +/* Return the RCS keyword expansion mode. For example "b" for binary. + Returns a pointer into storage which is allocated and freed along with + the rest of the RCS information; the caller should not modify this + storage. Returns NULL if the RCS file does not specify a keyword + expansion mode; for all other errors, die with a fatal error. */ +char * +RCS_getexpand (RCSNode *rcs) +{ + /* Since RCS_parsercsfile_i now reads expand, don't need to worry + about RCS_reparsercsfile. */ + assert (rcs != NULL); + return rcs->expand; +} + + + +/* Set keyword expansion mode to EXPAND. For example "b" for binary. */ +void +RCS_setexpand (RCSNode *rcs, const char *expand) +{ + /* Since RCS_parsercsfile_i now reads expand, don't need to worry + about RCS_reparsercsfile. */ + assert (rcs != NULL); + if (rcs->expand != NULL) + free (rcs->expand); + rcs->expand = xstrdup (expand); +} + + + +/* RCS keywords, and a matching enum. */ +struct rcs_keyword +{ + const char *string; + size_t len; + int expandit; +}; +#define KEYWORD_INIT(s) (s), sizeof (s) - 1 +static struct rcs_keyword keywords[] = +{ + { KEYWORD_INIT ("Author"), 1 }, + { KEYWORD_INIT ("Date"), 1 }, + { KEYWORD_INIT ("CVSHeader"), 1 }, + { KEYWORD_INIT ("Header"), 1 }, + { KEYWORD_INIT ("Id"), 1 }, + { KEYWORD_INIT ("Locker"), 1 }, + { KEYWORD_INIT ("Log"), 1 }, + { KEYWORD_INIT ("Name"), 1 }, + { KEYWORD_INIT ("RCSfile"), 1 }, + { KEYWORD_INIT ("Revision"), 1 }, + { KEYWORD_INIT ("Source"), 1 }, + { KEYWORD_INIT ("State"), 1 }, + { NULL, 0, 0 }, /* localid */ + { NULL, 0, 0 } +}; +enum keyword +{ + KEYWORD_AUTHOR = 0, + KEYWORD_DATE, + KEYWORD_CVSHEADER, + KEYWORD_HEADER, + KEYWORD_ID, + KEYWORD_LOCKER, + KEYWORD_LOG, + KEYWORD_NAME, + KEYWORD_RCSFILE, + KEYWORD_REVISION, + KEYWORD_SOURCE, + KEYWORD_STATE, + KEYWORD_LOCALID +}; +enum keyword keyword_local = KEYWORD_ID; + +/* Convert an RCS date string into a readable string. This is like + the RCS date2str function. */ + +static char * +printable_date (const char *rcs_date) +{ + int year, mon, mday, hour, min, sec; + char buf[100]; + + (void) sscanf (rcs_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); + return xstrdup (buf); +} + +/* Escape the characters in a string so that it can be included in an + RCS value. */ + +static char * +escape_keyword_value (const char *value, int *free_value) +{ + char *ret, *t; + const char *s; + + for (s = value; *s != '\0'; s++) + { + char c; + + c = *s; + if (c == '\t' + || c == '\n' + || c == '\\' + || c == ' ' + || c == '$') + { + break; + } + } + + if (*s == '\0') + { + *free_value = 0; + return (char *) value; + } + + ret = xmalloc (strlen (value) * 4 + 1); + *free_value = 1; + + for (s = value, t = ret; *s != '\0'; s++, t++) + { + switch (*s) + { + default: + *t = *s; + break; + case '\t': + *t++ = '\\'; + *t = 't'; + break; + case '\n': + *t++ = '\\'; + *t = 'n'; + break; + case '\\': + *t++ = '\\'; + *t = '\\'; + break; + case ' ': + *t++ = '\\'; + *t++ = '0'; + *t++ = '4'; + *t = '0'; + break; + case '$': + *t++ = '\\'; + *t++ = '0'; + *t++ = '4'; + *t = '4'; + break; + } + } + + *t = '\0'; + + return ret; +} + +/* Expand RCS keywords in the memory buffer BUF of length LEN. This + applies to file RCS and version VERS. If NAME is not NULL, and is + not a numeric revision, then it is the symbolic tag used for the + checkout. EXPAND indicates how to expand the keywords. This + function sets *RETBUF and *RETLEN to the new buffer and length. + This function may modify the buffer BUF. If BUF != *RETBUF, then + RETBUF is a newly allocated buffer. */ + +static void +expand_keywords (RCSNode *rcs, RCSVers *ver, const char *name, const char *log, size_t loglen, enum kflag expand, char *buf, size_t len, char **retbuf, size_t *retlen) +{ + struct expand_buffer + { + struct expand_buffer *next; + char *data; + size_t len; + int free_data; + } *ebufs = NULL; + struct expand_buffer *ebuf_last = NULL; + size_t ebuf_len = 0; + char *locker; + char *srch, *srch_next; + size_t srch_len; + + if (expand == KFLAG_O || expand == KFLAG_B) + { + *retbuf = buf; + *retlen = len; + return; + } + + /* If we are using -kkvl, dig out the locker information if any. */ + locker = NULL; + if (expand == KFLAG_KVL) + { + Node *lock; + lock = findnode (RCS_getlocks(rcs), ver->version); + if (lock != NULL) + locker = xstrdup (lock->data); + } + + /* RCS keywords look like $STRING$ or $STRING: VALUE$. */ + srch = buf; + srch_len = len; + while ((srch_next = memchr (srch, '$', srch_len)) != NULL) + { + char *s, *send; + size_t slen; + const struct rcs_keyword *keyword; + enum keyword kw; + char *value; + int free_value; + char *sub; + size_t sublen; + + srch_len -= (srch_next + 1) - srch; + srch = srch_next + 1; + + /* Look for the first non alphabetic character after the '$'. */ + send = srch + srch_len; + for (s = srch; s < send; s++) + if (! isalpha ((unsigned char) *s)) + break; + + /* If the first non alphabetic character is not '$' or ':', + then this is not an RCS keyword. */ + if (s == send || (*s != '$' && *s != ':')) + continue; + + /* See if this is one of the keywords. */ + slen = s - srch; + for (keyword = keywords; keyword->string != NULL; keyword++) + { + if (keyword->expandit + && keyword->len == slen + && strncmp (keyword->string, srch, slen) == 0) + { + break; + } + } + if (keyword->string == NULL) + continue; + + kw = (enum keyword) (keyword - keywords); + + /* If the keyword ends with a ':', then the old value consists + of the characters up to the next '$'. If there is no '$' + before the end of the line, though, then this wasn't an RCS + keyword after all. */ + if (*s == ':') + { + for (; s < send; s++) + if (*s == '$' || *s == '\n') + break; + if (s == send || *s != '$') + continue; + } + + /* At this point we must replace the string from SRCH to S + with the expansion of the keyword KW. */ + + /* Get the value to use. */ + free_value = 0; + if (expand == KFLAG_K) + value = NULL; + else + { + switch (kw) + { + default: + abort (); + + case KEYWORD_AUTHOR: + value = ver->author; + break; + + case KEYWORD_DATE: + value = printable_date (ver->date); + free_value = 1; + break; + + case KEYWORD_CVSHEADER: + case KEYWORD_HEADER: + case KEYWORD_ID: + case KEYWORD_LOCALID: + { + const char *path; + int free_path; + char *date; + char *old_path; + + old_path = NULL; + if (kw == KEYWORD_HEADER || + (kw == KEYWORD_LOCALID && + keyword_local == KEYWORD_HEADER)) + path = rcs->path; + else if (kw == KEYWORD_CVSHEADER || + (kw == KEYWORD_LOCALID && + keyword_local == KEYWORD_CVSHEADER)) + path = getfullCVSname(rcs->path, &old_path); + else + path = last_component (rcs->path); + path = escape_keyword_value (path, &free_path); + date = printable_date (ver->date); + value = xmalloc (strlen (path) + + strlen (ver->version) + + strlen (date) + + strlen (ver->author) + + strlen (ver->state) + + (locker == NULL ? 0 : strlen (locker)) + + 20); + + sprintf (value, "%s %s %s %s %s%s%s", + path, ver->version, date, ver->author, + ver->state, + locker != NULL ? " " : "", + locker != NULL ? locker : ""); + if (free_path) + /* If free_path is set then we know we allocated path + * and we can discard the const. + */ + free ((char *)path); + if (old_path) + free (old_path); + free (date); + free_value = 1; + } + break; + + case KEYWORD_LOCKER: + value = locker; + break; + + case KEYWORD_LOG: + case KEYWORD_RCSFILE: + value = escape_keyword_value (last_component (rcs->path), + &free_value); + break; + + case KEYWORD_NAME: + if (name != NULL && ! isdigit ((unsigned char) *name)) + value = (char *) name; + else + value = NULL; + break; + + case KEYWORD_REVISION: + value = ver->version; + break; + + case KEYWORD_SOURCE: + value = escape_keyword_value (rcs->path, &free_value); + break; + + case KEYWORD_STATE: + value = ver->state; + break; + } + } + + sub = xmalloc (keyword->len + + (value == NULL ? 0 : strlen (value)) + + 10); + if (expand == KFLAG_V) + { + /* Decrement SRCH and increment S to remove the $ + characters. */ + --srch; + ++srch_len; + ++s; + sublen = 0; + } + else + { + strcpy (sub, keyword->string); + sublen = strlen (keyword->string); + if (expand != KFLAG_K) + { + sub[sublen] = ':'; + sub[sublen + 1] = ' '; + sublen += 2; + } + } + if (value != NULL) + { + strcpy (sub + sublen, value); + sublen += strlen (value); + } + if (expand != KFLAG_V && expand != KFLAG_K) + { + sub[sublen] = ' '; + ++sublen; + sub[sublen] = '\0'; + } + + if (free_value) + free (value); + + /* The Log keyword requires special handling. This behaviour + is taken from RCS 5.7. The special log message is what RCS + uses for ci -k. */ + if (kw == KEYWORD_LOG + && (sizeof "checked in with -k by " <= loglen + || log == NULL + || strncmp (log, "checked in with -k by ", + sizeof "checked in with -k by " - 1) != 0)) + { + char *start; + char *leader; + size_t leader_len, leader_sp_len; + const char *logend; + const char *snl; + int cnl; + char *date; + const char *sl; + + /* We are going to insert the trailing $ ourselves, before + the log message, so we must remove it from S, if we + haven't done so already. */ + if (expand != KFLAG_V) + ++s; + + /* CVS never has empty log messages, but old RCS files might. */ + if (log == NULL) + log = ""; + + /* Find the start of the line. */ + start = srch; + while (start > buf && start[-1] != '\n') + --start; + + /* Copy the start of the line to use as a comment leader. */ + leader_len = srch - start; + if (expand != KFLAG_V) + --leader_len; + leader = xmalloc (leader_len); + memcpy (leader, start, leader_len); + leader_sp_len = leader_len; + while (leader_sp_len > 0 && leader[leader_sp_len - 1] == ' ') + --leader_sp_len; + + /* RCS does some checking for an old style of Log here, + but we don't bother. RCS issues a warning if it + changes anything. */ + + /* Count the number of newlines in the log message so that + we know how many copies of the leader we will need. */ + cnl = 0; + logend = log + loglen; + for (snl = log; snl < logend; snl++) + if (*snl == '\n') + ++cnl; + + date = printable_date (ver->date); + sub = xrealloc (sub, + (sublen + + sizeof "Revision" + + strlen (ver->version) + + strlen (date) + + strlen (ver->author) + + loglen + + (cnl + 2) * leader_len + + 20)); + if (expand != KFLAG_V) + { + sub[sublen] = '$'; + ++sublen; + } + sub[sublen] = '\n'; + ++sublen; + memcpy (sub + sublen, leader, leader_len); + sublen += leader_len; + sprintf (sub + sublen, "Revision %s %s %s\n", + ver->version, date, ver->author); + sublen += strlen (sub + sublen); + free (date); + + sl = log; + while (sl < logend) + { + if (*sl == '\n') + { + memcpy (sub + sublen, leader, leader_sp_len); + sublen += leader_sp_len; + sub[sublen] = '\n'; + ++sublen; + ++sl; + } + else + { + const char *slnl; + + memcpy (sub + sublen, leader, leader_len); + sublen += leader_len; + for (slnl = sl; slnl < logend && *slnl != '\n'; ++slnl) + ; + if (slnl < logend) + ++slnl; + memcpy (sub + sublen, sl, slnl - sl); + sublen += slnl - sl; + sl = slnl; + } + } + + memcpy (sub + sublen, leader, leader_sp_len); + sublen += leader_sp_len; + + free (leader); + } + + /* Now SUB contains a string which is to replace the string + from SRCH to S. SUBLEN is the length of SUB. */ + + if (srch + sublen == s) + { + memcpy (srch, sub, sublen); + free (sub); + } + else + { + struct expand_buffer *ebuf; + + /* We need to change the size of the buffer. We build a + list of expand_buffer structures. Each expand_buffer + structure represents a portion of the final output. We + concatenate them back into a single buffer when we are + done. This minimizes the number of potentially large + buffer copies we must do. */ + + if (ebufs == NULL) + { + ebufs = (struct expand_buffer *) xmalloc (sizeof *ebuf); + ebufs->next = NULL; + ebufs->data = buf; + ebufs->free_data = 0; + ebuf_len = srch - buf; + ebufs->len = ebuf_len; + ebuf_last = ebufs; + } + else + { + assert (srch >= ebuf_last->data); + assert (srch <= ebuf_last->data + ebuf_last->len); + ebuf_len -= ebuf_last->len - (srch - ebuf_last->data); + ebuf_last->len = srch - ebuf_last->data; + } + + ebuf = (struct expand_buffer *) xmalloc (sizeof *ebuf); + ebuf->data = sub; + ebuf->len = sublen; + ebuf->free_data = 1; + ebuf->next = NULL; + ebuf_last->next = ebuf; + ebuf_last = ebuf; + ebuf_len += sublen; + + ebuf = (struct expand_buffer *) xmalloc (sizeof *ebuf); + ebuf->data = s; + ebuf->len = srch_len - (s - srch); + ebuf->free_data = 0; + ebuf->next = NULL; + ebuf_last->next = ebuf; + ebuf_last = ebuf; + ebuf_len += srch_len - (s - srch); + } + + srch_len -= (s - srch); + srch = s; + } + + if (locker != NULL) + free (locker); + + if (ebufs == NULL) + { + *retbuf = buf; + *retlen = len; + } + else + { + char *ret; + + ret = xmalloc (ebuf_len); + *retbuf = ret; + *retlen = ebuf_len; + while (ebufs != NULL) + { + struct expand_buffer *next; + + memcpy (ret, ebufs->data, ebufs->len); + ret += ebufs->len; + if (ebufs->free_data) + free (ebufs->data); + next = ebufs->next; + free (ebufs); + ebufs = next; + } + } +} + + + +/* Check out a revision from an RCS file. + + If PFN is not NULL, then ignore WORKFILE and SOUT. Call PFN zero + or more times with the contents of the file. CALLERDAT is passed, + uninterpreted, to PFN. (The current code will always call PFN + exactly once for a non empty file; however, the current code + assumes that it can hold the entire file contents in memory, which + is not a good assumption, and might change in the future). + + Otherwise, if WORKFILE is not NULL, check out the revision to + WORKFILE. However, if WORKFILE is not NULL, and noexec is set, + then don't do anything. + + Otherwise, if WORKFILE is NULL, check out the revision to SOUT. If + SOUT is RUN_TTY, then write the contents of the revision to + standard output. When using SOUT, the output is generally a + temporary file; don't bother to get the file modes correct. + + REV is the numeric revision to check out. It may be NULL, which + means to check out the head of the default branch. + + If NAMETAG is not NULL, and is not a numeric revision, then it is + the tag that should be used when expanding the RCS Name keyword. + + OPTIONS is a string such as "-kb" or "-kv" for keyword expansion + options. It may be NULL to use the default expansion mode of the + file, typically "-kkv". + + On an error which prevented checking out the file, either print a + nonfatal error and return 1, or give a fatal error. On success, + return 0. */ + +/* This function mimics the behavior of `rcs co' almost exactly. The + chief difference is in its support for preserving file ownership, + permissions, and special files across checkin and checkout -- see + comments in RCS_checkin for some issues about this. -twp */ + +int +RCS_checkout (RCSNode *rcs, const char *workfile, const char *rev, + const char *nametag, const char *options, const char *sout, + RCSCHECKOUTPROC pfn, void *callerdat) +{ + int free_rev = 0; + enum kflag expand; + FILE *fp, *ofp; + struct stat sb; + struct rcsbuffer rcsbuf; + char *key; + char *value; + size_t len; + int free_value = 0; + char *log = NULL; + size_t loglen; + Node *vp = NULL; +#ifdef PRESERVE_PERMISSIONS_SUPPORT + uid_t rcs_owner = (uid_t) -1; + gid_t rcs_group = (gid_t) -1; + mode_t rcs_mode; + int change_rcs_owner_or_group = 0; + int change_rcs_mode = 0; + int special_file = 0; + unsigned long devnum_long; + dev_t devnum = 0; +#endif + + TRACE ( 1, "RCS_checkout (%s, %s, %s, %s, %s)", + rcs->path, + rev != NULL ? rev : "", + nametag != NULL ? nametag : "", + options != NULL ? options : "", + (pfn != NULL ? "(function)" + : (workfile != NULL ? workfile + : (sout != RUN_TTY ? sout + : "(stdout)" ) ) ) ); + + assert (rev == NULL || isdigit ((unsigned char) *rev)); + + if (noexec && workfile != NULL) + return 0; + + assert (sout == RUN_TTY || workfile == NULL); + assert (pfn == NULL || (sout == RUN_TTY && workfile == NULL)); + + /* Some callers, such as Checkin or remove_file, will pass us a + branch. */ + if (rev != NULL && (numdots (rev) & 1) == 0) + { + rev = RCS_getbranch (rcs, rev, 1); + if (rev == NULL) + error (1, 0, "internal error: bad branch tag in checkout"); + free_rev = 1; + } + + if (rev == NULL || STREQ (rev, rcs->head)) + { + int gothead; + + /* We want the head revision. Try to read it directly. */ + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, &fp, &rcsbuf); + else + rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf); + + gothead = 0; + if (! rcsbuf_getrevnum (&rcsbuf, &key)) + error (1, 0, "unexpected EOF reading %s", rcs->path); + while (rcsbuf_getkey (&rcsbuf, &key, &value)) + { + if (STREQ (key, "log")) + log = rcsbuf_valcopy (&rcsbuf, value, 0, &loglen); + else if (STREQ (key, "text")) + { + gothead = 1; + break; + } + } + + if (! gothead) + { + error (0, 0, "internal error: cannot find head text"); + if (free_rev) + /* It's okay to discard the const when free_rev is set, because + * we know we allocated it in this function. + */ + free ((char *)rev); + return 1; + } + + rcsbuf_valpolish (&rcsbuf, value, 0, &len); + + if (fstat (fileno (fp), &sb) < 0) + error (1, errno, "cannot fstat %s", rcs->path); + + rcsbuf_cache (rcs, &rcsbuf); + } + else + { + struct rcsbuffer *rcsbufp; + + /* It isn't the head revision of the trunk. We'll need to + walk through the deltas. */ + + fp = NULL; + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, &fp, &rcsbuf); + + if (fp == NULL) + { + /* If RCS_deltas didn't close the file, we could use fstat + here too. Probably should change it thusly.... */ + if( CVS_STAT( rcs->path, &sb ) < 0 ) + error (1, errno, "cannot stat %s", rcs->path); + rcsbufp = NULL; + } + else + { + if (fstat (fileno (fp), &sb) < 0) + error (1, errno, "cannot fstat %s", rcs->path); + rcsbufp = &rcsbuf; + } + + RCS_deltas (rcs, fp, rcsbufp, rev, RCS_FETCH, &value, &len, + &log, &loglen); + free_value = 1; + } + + /* If OPTIONS is NULL or the empty string, then the old code would + invoke the RCS co program with no -k option, which means that + co would use the string we have stored in rcs->expand. */ + if ((options == NULL || options[0] == '\0') && rcs->expand == NULL) + expand = KFLAG_KV; + else + { + const char *ouroptions; + const char * const *cpp; + + if (options != NULL && options[0] != '\0') + { + assert (options[0] == '-' && options[1] == 'k'); + ouroptions = options + 2; + } + else + ouroptions = rcs->expand; + + for (cpp = kflags; *cpp != NULL; cpp++) + if (STREQ (*cpp, ouroptions)) + break; + + if (*cpp != NULL) + expand = (enum kflag) (cpp - kflags); + else + { + error (0, 0, + "internal error: unsupported substitution string -k%s", + ouroptions); + expand = KFLAG_KV; + } + } + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Handle special files and permissions, if that is desired. */ + if (preserve_perms) + { + RCSVers *vers; + Node *info; + + vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); + if (vp == NULL) + error (1, 0, "internal error: no revision information for %s", + rev == NULL ? rcs->head : rev); + vers = vp->data; + + /* First we look for symlinks, which are simplest to handle. */ + info = findnode (vers->other_delta, "symlink"); + if (info != NULL) + { + char *dest; + + if (pfn != NULL || (workfile == NULL && sout == RUN_TTY)) + error (1, 0, "symbolic link %s:%s cannot be piped", + rcs->path, vers->version); + if (workfile == NULL) + dest = sout; + else + dest = workfile; + + /* Remove `dest', just in case. It's okay to get ENOENT here, + since we just want the file not to be there. (TODO: decide + whether it should be considered an error for `dest' to exist + at this point. If so, the unlink call should be removed and + `symlink' should signal the error. -twp) */ + if (CVS_UNLINK (dest) < 0 && !existence_error (errno)) + error (1, errno, "cannot remove %s", dest); + if (symlink (info->data, dest) < 0) + error (1, errno, "cannot create symbolic link from %s to %s", + dest, (char *)info->data); + if (free_value) + free (value); + if (free_rev) + /* It's okay to discard the const when free_rev is set, because + * we know we allocated it in this function. + */ + free ((char *)rev); + return 0; + } + + /* Next, we look at this file's hardlinks field, and see whether + it is linked to any other file that has been checked out. + If so, we don't do anything else -- just link it to that file. + + If we are checking out a file to a pipe or temporary storage, + none of this should matter. Hence the `workfile != NULL' + wrapper around the whole thing. -twp */ + + if (workfile != NULL) + { + List *links = vers->hardlinks; + if (links != NULL) + { + Node *uptodate_link; + + /* For each file in the hardlinks field, check to see + if it exists, and if so, if it has been checked out + this iteration. When walklist returns, uptodate_link + should point to a hardlist node representing a file + in `links' which has recently been checked out, or + NULL if no file in `links' has yet been checked out. */ + + uptodate_link = NULL; + (void) walklist (links, find_checkedout_proc, &uptodate_link); + dellist (&links); + + /* If we've found a file that `workfile' is supposed to be + linked to, and it has been checked out since CVS was + invoked, then simply link workfile to that file and return. + + If one of these conditions is not met, then + workfile is the first one in its hardlink group to + be checked out, and we must continue with a full + checkout. */ + + if (uptodate_link != NULL) + { + struct hardlink_info *hlinfo = uptodate_link->data; + + if (link (uptodate_link->key, workfile) < 0) + error (1, errno, "cannot link %s to %s", + workfile, uptodate_link->key); + hlinfo->checked_out = 1; /* probably unnecessary */ + if (free_value) + free (value); + if (free_rev) + /* It's okay to discard the const when free_rev is set, + * because we know we allocated it in this function. + */ + free ((char *)rev); + return 0; + } + } + } + + info = findnode (vers->other_delta, "owner"); + if (info != NULL) + { + change_rcs_owner_or_group = 1; + rcs_owner = (uid_t) strtoul (info->data, NULL, 10); + } + info = findnode (vers->other_delta, "group"); + if (info != NULL) + { + change_rcs_owner_or_group = 1; + rcs_group = (gid_t) strtoul (info->data, NULL, 10); + } + info = findnode (vers->other_delta, "permissions"); + if (info != NULL) + { + change_rcs_mode = 1; + rcs_mode = (mode_t) strtoul (info->data, NULL, 8); + } + info = findnode (vers->other_delta, "special"); + if (info != NULL) + { + /* If the size of `devtype' changes, fix the sscanf call also */ + char devtype[16]; + + if (sscanf (info->data, "%15s %lu", + devtype, &devnum_long) < 2) + error (1, 0, "%s:%s has bad `special' newphrase %s", + workfile, vers->version, (char *)info->data); + devnum = devnum_long; + if (STREQ (devtype, "character")) + special_file = S_IFCHR; + else if (STREQ (devtype, "block")) + special_file = S_IFBLK; + else + error (0, 0, "%s is a special file of unsupported type `%s'", + workfile, (char *)info->data); + } + } +#endif /* PRESERVE_PERMISSIONS_SUPPORT */ + + if (expand != KFLAG_O && expand != KFLAG_B) + { + char *newvalue; + + /* Don't fetch the delta node again if we already have it. */ + if (vp == NULL) + { + vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); + if (vp == NULL) + error (1, 0, "internal error: no revision information for %s", + rev == NULL ? rcs->head : rev); + } + + expand_keywords (rcs, vp->data, nametag, log, loglen, + expand, value, len, &newvalue, &len); + + if (newvalue != value) + { + if (free_value) + free (value); + value = newvalue; + free_value = 1; + } + } + + if (free_rev) + /* It's okay to discard the const when free_rev is set, because + * we know we allocated it in this function. + */ + free ((char *)rev); + + if (log != NULL) + { + free (log); + log = NULL; + } + + if (pfn != NULL) + { +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (special_file) + error (1, 0, "special file %s cannot be piped to anything", + rcs->path); +#endif + /* The PFN interface is very simple to implement right now, as + we always have the entire file in memory. */ + if (len != 0) + pfn (callerdat, value, len); + } +#ifdef PRESERVE_PERMISSIONS_SUPPORT + else if (special_file) + { +# ifdef HAVE_MKNOD + char *dest; + + /* Can send either to WORKFILE or to SOUT, as long as SOUT is + not RUN_TTY. */ + dest = workfile; + if (dest == NULL) + { + if (sout == RUN_TTY) + error (1, 0, "special file %s cannot be written to stdout", + rcs->path); + dest = sout; + } + + /* Unlink `dest', just in case. It's okay if this provokes a + ENOENT error. */ + if (CVS_UNLINK (dest) < 0 && existence_error (errno)) + error (1, errno, "cannot remove %s", dest); + if (mknod (dest, special_file, devnum) < 0) + error (1, errno, "could not create special file %s", + dest); +# else + error (1, 0, +"cannot create %s: unable to create special files on this system", +workfile); +# endif + } +#endif + else + { + /* Not a special file: write to WORKFILE or SOUT. */ + if (workfile == NULL) + { + if (sout == RUN_TTY) + ofp = stdout; + else + { + /* Symbolic links should be removed before replacement, so that + `fopen' doesn't follow the link and open the wrong file. */ + if (islink (sout)) + if (unlink_file (sout) < 0) + error (1, errno, "cannot remove %s", sout); + ofp = CVS_FOPEN (sout, expand == KFLAG_B ? "wb" : "w"); + if (ofp == NULL) + error (1, errno, "cannot open %s", sout); + } + } + else + { + /* Output is supposed to go to WORKFILE, so we should open that + file. Symbolic links should be removed first (see above). */ + if (islink (workfile)) + if (unlink_file (workfile) < 0) + error (1, errno, "cannot remove %s", workfile); + + ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w"); + + /* If the open failed because the existing workfile was not + writable, try to chmod the file and retry the open. */ + if (ofp == NULL && errno == EACCES + && isfile (workfile) && !iswritable (workfile)) + { + xchmod (workfile, 1); + ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w"); + } + + if (ofp == NULL) + { + error (0, errno, "cannot open %s", workfile); + if (free_value) + free (value); + return 1; + } + } + + if (workfile == NULL && sout == RUN_TTY) + { + if (expand == KFLAG_B) + cvs_output_binary (value, len); + else + { + /* cvs_output requires the caller to check for zero + length. */ + if (len > 0) + cvs_output (value, len); + } + } + else + { + /* NT 4.0 is said to have trouble writing 2099999 bytes + (for example) in a single fwrite. So break it down + (there is no need to be writing that much at once + anyway; it is possible that LARGEST_FWRITE should be + somewhat larger for good performance, but for testing I + want to start with a small value until/unless a bigger + one proves useful). */ +#define LARGEST_FWRITE 8192 + size_t nleft = len; + size_t nstep = (len < LARGEST_FWRITE ? len : LARGEST_FWRITE); + char *p = value; + + while (nleft > 0) + { + if (fwrite (p, 1, nstep, ofp) != nstep) + { + error (0, errno, "cannot write %s", + (workfile != NULL + ? workfile + : (sout != RUN_TTY ? sout : "stdout"))); + if (free_value) + free (value); + return 1; + } + p += nstep; + nleft -= nstep; + if (nleft < nstep) + nstep = nleft; + } + } + } + + if (free_value) + free (value); + + if (workfile != NULL) + { + int ret; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (!special_file && fclose (ofp) < 0) + { + error (0, errno, "cannot close %s", workfile); + return 1; + } + + if (change_rcs_owner_or_group) + { + if (chown (workfile, rcs_owner, rcs_group) < 0) + error (0, errno, "could not change owner or group of %s", + workfile); + } + + ret = chmod (workfile, + change_rcs_mode + ? rcs_mode + : sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)); +#else + if (fclose (ofp) < 0) + { + error (0, errno, "cannot close %s", workfile); + return 1; + } + + ret = chmod (workfile, + sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)); +#endif + if (ret < 0) + { + error (0, errno, "cannot change mode of file %s", + workfile); + } + } + else if (sout != RUN_TTY) + { + if ( +#ifdef PRESERVE_PERMISSIONS_SUPPORT + !special_file && +#endif + fclose (ofp) < 0) + { + error (0, errno, "cannot close %s", sout); + return 1; + } + } + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If we are in the business of preserving hardlinks, then + mark this file as having been checked out. */ + if (preserve_perms && workfile != NULL) + update_hardlink_info (workfile); +#endif + + return 0; +} + +static RCSVers *RCS_findlock_or_tip (RCSNode *rcs); + +/* Find the delta currently locked by the user. From the `ci' man page: + + "If rev is omitted, ci tries to derive the new revision + number from the caller's last lock. If the caller has + locked the tip revision of a branch, the new revision is + appended to that branch. The new revision number is + obtained by incrementing the tip revision number. If the + caller locked a non-tip revision, a new branch is started + at that revision by incrementing the highest branch number + at that revision. The default initial branch and level + numbers are 1. + + If rev is omitted and the caller has no lock, but owns the + file and locking is not set to strict, then the revision + is appended to the default branch (normally the trunk; see + the -b option of rcs(1))." + + RCS_findlock_or_tip finds the unique revision locked by the caller + and returns its delta node. If the caller has not locked any + revisions (and is permitted to commit to an unlocked delta, as + described above), return the tip of the default branch. */ + +static RCSVers * +RCS_findlock_or_tip (RCSNode *rcs) +{ + char *user = getcaller(); + Node *lock, *p; + List *locklist; + + /* Find unique delta locked by caller. This code is very similar + to the code in RCS_unlock -- perhaps it could be abstracted + into a RCS_findlock function. */ + locklist = RCS_getlocks (rcs); + lock = NULL; + for (p = locklist->list->next; p != locklist->list; p = p->next) + { + if (STREQ (p->data, user)) + { + if (lock != NULL) + { + error (0, 0, "\ +%s: multiple revisions locked by %s; please specify one", rcs->path, user); + return NULL; + } + lock = p; + } + } + + if (lock != NULL) + { + /* Found an old lock, but check that the revision still exists. */ + p = findnode (rcs->versions, lock->key); + if (p == NULL) + { + error (0, 0, "%s: can't unlock nonexistent revision %s", + rcs->path, + lock->key); + return NULL; + } + return p->data; + } + + /* No existing lock. The RCS rule is that this is an error unless + locking is nonstrict AND the file is owned by the current + user. Trying to determine the latter is a portability nightmare + in the face of NT, VMS, AFS, and other systems with non-unix-like + ideas of users and owners. In the case of CVS, we should never get + here (as long as the traditional behavior of making sure to call + RCS_lock persists). Anyway, we skip the RCS error checks + and just return the default branch or head. The reasoning is that + those error checks are to make users lock before a checkin, and we do + that in other ways if at all anyway (e.g. rcslock.pl). */ + + p = findnode (rcs->versions, RCS_getbranch (rcs, rcs->branch, 0)); + return p->data; +} + +/* Revision number string, R, must contain a `.'. + Return a newly-malloc'd copy of the prefix of R up + to but not including the final `.'. */ + +static char * +truncate_revnum (const char *r) +{ + size_t len; + char *new_r; + char *dot = strrchr (r, '.'); + + assert (dot); + len = dot - r; + new_r = xmalloc (len + 1); + memcpy (new_r, r, len); + *(new_r + len) = '\0'; + return new_r; +} + +/* Revision number string, R, must contain a `.'. + R must be writable. Replace the rightmost `.' in R with + the NUL byte and return a pointer to that NUL byte. */ + +static char * +truncate_revnum_in_place (char *r) +{ + char *dot = strrchr (r, '.'); + assert (dot); + *dot = '\0'; + return dot; +} + +/* Revision number strings, R and S, must each contain a `.'. + R and S must be writable and must have the same number of dots. + Truncate R and S for the comparison, then restored them to their + original state. + Return the result (see compare_revnums) of comparing R and S + ignoring differences in any component after the rightmost `.'. */ + +static int +compare_truncated_revnums (char *r, char *s) +{ + char *r_dot = truncate_revnum_in_place (r); + char *s_dot = truncate_revnum_in_place (s); + int cmp; + + assert (numdots (r) == numdots (s)); + + cmp = compare_revnums (r, s); + + *r_dot = '.'; + *s_dot = '.'; + + return cmp; +} + +/* Return a malloc'd copy of the string representing the highest branch + number on BRANCHNODE. If there are no branches on BRANCHNODE, return NULL. + FIXME: isn't the max rev always the last one? + If so, we don't even need a loop. */ + +static char *max_rev (const RCSVers *); + +static char * +max_rev (const RCSVers *branchnode) +{ + Node *head; + Node *bp; + char *max; + + if (branchnode->branches == NULL) + { + return NULL; + } + + max = NULL; + head = branchnode->branches->list; + for (bp = head->next; bp != head; bp = bp->next) + { + if (max == NULL || compare_truncated_revnums (max, bp->key) < 0) + { + max = bp->key; + } + } + assert (max); + + return truncate_revnum (max); +} + +/* Create BRANCH in RCS's delta tree. BRANCH may be either a branch + number or a revision number. In the former case, create the branch + with the specified number; in the latter case, create a new branch + rooted at node BRANCH with a higher branch number than any others. + Return the number of the tip node on the new branch. */ + +static char * +RCS_addbranch (RCSNode *rcs, const char *branch) +{ + char *branchpoint, *newrevnum; + Node *nodep, *bp; + Node *marker; + RCSVers *branchnode; + + /* Append to end by default. */ + marker = NULL; + + branchpoint = xstrdup (branch); + if ((numdots (branchpoint) & 1) == 0) + { + truncate_revnum_in_place (branchpoint); + } + + /* Find the branch rooted at BRANCHPOINT. */ + nodep = findnode (rcs->versions, branchpoint); + if (nodep == NULL) + { + error (0, 0, "%s: can't find branch point %s", rcs->path, branchpoint); + free (branchpoint); + return NULL; + } + free (branchpoint); + branchnode = nodep->data; + + /* If BRANCH was a full branch number, make sure it is higher than MAX. */ + if ((numdots (branch) & 1) == 1) + { + if (branchnode->branches == NULL) + { + /* We have to create the first branch on this node, which means + appending ".2" to the revision number. */ + newrevnum = (char *) xmalloc (strlen (branch) + 3); + strcpy (newrevnum, branch); + strcat (newrevnum, ".2"); + } + else + { + char *max = max_rev (branchnode); + assert (max); + newrevnum = increment_revnum (max); + free (max); + } + } + else + { + newrevnum = xstrdup (branch); + + if (branchnode->branches != NULL) + { + Node *head; + Node *bp; + + /* Find the position of this new branch in the sorted list + of branches. */ + head = branchnode->branches->list; + for (bp = head->next; bp != head; bp = bp->next) + { + char *dot; + int found_pos; + + /* The existing list must be sorted on increasing revnum. */ + assert (bp->next == head + || compare_truncated_revnums (bp->key, + bp->next->key) < 0); + dot = truncate_revnum_in_place (bp->key); + found_pos = (compare_revnums (branch, bp->key) < 0); + *dot = '.'; + + if (found_pos) + { + break; + } + } + marker = bp; + } + } + + newrevnum = (char *) xrealloc (newrevnum, strlen (newrevnum) + 3); + strcat (newrevnum, ".1"); + + /* Add this new revision number to BRANCHPOINT's branches list. */ + if (branchnode->branches == NULL) + branchnode->branches = getlist(); + bp = getnode(); + bp->key = xstrdup (newrevnum); + + /* Append to the end of the list by default, that is, just before + the header node, `list'. */ + if (marker == NULL) + marker = branchnode->branches->list; + + { + int fail; + fail = insert_before (branchnode->branches, marker, bp); + assert (!fail); + } + + return newrevnum; +} + +/* Check in to RCSFILE with revision REV (which must be greater than + the largest revision) and message MESSAGE (which is checked for + validity). 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. + If FLAGS & RCS_FLAGS_KEEPFILE, don't unlink the working file; + unlinking the working file is standard RCS behavior, but is rarely + appropriate for CVS. + + This function should almost exactly mimic the behavior of `rcs ci'. The + principal point of difference is the support here for preserving file + ownership and permissions in the delta nodes. This is not a clean + solution -- precisely because it diverges from RCS's behavior -- but + it doesn't seem feasible to do this anywhere else in the code. [-twp] + + 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 (RCSNode *rcs, const char *workfile_in, const char *message, + const char *rev, int flags) +{ + RCSVers *delta, *commitpt; + Deltatext *dtext; + Node *nodep; + char *tmpfile, *changefile; + char *diffopts; + size_t bufsize; + int status, checkin_quiet; + struct tm *ftm; + time_t modtime; + int adding_branch = 0; + char *workfile = xstrdup (workfile_in); +#ifdef PRESERVE_PERMISSIONS_SUPPORT + struct stat sb; +#endif + + commitpt = NULL; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + /* Get basename of working file. Is there a library function to + do this? I couldn't find one. -twp */ + if (workfile == NULL) + { + char *p; + int extlen = strlen (RCSEXT); + workfile = xstrdup (last_component (rcs->path)); + p = workfile + (strlen (workfile) - extlen); + assert (strncmp (p, RCSEXT, extlen) == 0); + *p = '\0'; + } + + /* If the filename is a symbolic link, follow it and replace it + with the destination of the link. We need to do this before + calling rcs_internal_lockfile, or else we won't put the lock in + the right place. */ + resolve_symlink (&(rcs->path)); + + checkin_quiet = flags & RCS_FLAGS_QUIET; + if (!(checkin_quiet || really_quiet)) + { + cvs_output (rcs->path, 0); + cvs_output (" <-- ", 7); + cvs_output (workfile, 0); + cvs_output ("\n", 1); + } + + /* Create new delta node. */ + delta = (RCSVers *) xmalloc (sizeof (RCSVers)); + memset (delta, 0, sizeof (RCSVers)); + delta->author = xstrdup (getcaller ()); + if (flags & RCS_FLAGS_MODTIME) + { + struct stat ws; + if( CVS_STAT( workfile, &ws ) < 0 ) + { + error (1, errno, "cannot stat %s", workfile); + } + modtime = ws.st_mtime; + } + else + (void) time (&modtime); + ftm = gmtime (&modtime); + delta->date = (char *) xmalloc (MAXDATELEN); + (void) sprintf (delta->date, DATEFORM, + ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900), + ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour, + ftm->tm_min, ftm->tm_sec); + if (flags & RCS_FLAGS_DEAD) + { + delta->state = xstrdup (RCSDEAD); + delta->dead = 1; + } + else + delta->state = xstrdup ("Exp"); + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If permissions should be preserved on this project, then + save the permission info. */ + if (preserve_perms) + { + Node *np; + char buf[64]; /* static buffer should be safe: see usage. -twp */ + + delta->other_delta = getlist(); + + if (CVS_LSTAT (workfile, &sb) < 0) + error (1, errno, "cannot lstat %s", workfile); + + if (S_ISLNK (sb.st_mode)) + { + np = getnode(); + np->type = RCSFIELD; + np->key = xstrdup ("symlink"); + np->data = xreadlink (workfile); + addnode (delta->other_delta, np); + } + else + { + (void) sprintf (buf, "%u", sb.st_uid); + np = getnode(); + np->type = RCSFIELD; + np->key = xstrdup ("owner"); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); + + (void) sprintf (buf, "%u", sb.st_gid); + np = getnode(); + np->type = RCSFIELD; + np->key = xstrdup ("group"); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); + + (void) sprintf (buf, "%o", sb.st_mode & 07777); + np = getnode(); + np->type = RCSFIELD; + np->key = xstrdup ("permissions"); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); + + /* Save device number. */ + switch (sb.st_mode & S_IFMT) + { + case S_IFREG: break; + case S_IFCHR: + case S_IFBLK: +# ifdef HAVE_STRUCT_STAT_ST_RDEV + np = getnode(); + np->type = RCSFIELD; + np->key = xstrdup ("special"); + sprintf (buf, "%s %lu", + ((sb.st_mode & S_IFMT) == S_IFCHR + ? "character" : "block"), + (unsigned long) sb.st_rdev); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); +# else + error (0, 0, +"can't preserve %s: unable to save device files on this system", +workfile); +# endif + break; + + default: + error (0, 0, "special file %s has unknown type", workfile); + } + + /* Save hardlinks. */ + delta->hardlinks = list_linked_files_on_disk (workfile); + } + } +#endif + + /* Create a new deltatext node. */ + dtext = (Deltatext *) xmalloc (sizeof (Deltatext)); + memset (dtext, 0, sizeof (Deltatext)); + + dtext->log = make_message_rcsvalid (message); + + /* If the delta tree is empty, then there's nothing to link the + new delta into. So make a new delta tree, snarf the working + file contents, and just write the new RCS file. */ + if (rcs->head == NULL) + { + char *newrev; + FILE *fout; + + /* Figure out what the first revision number should be. */ + if (rev == NULL || *rev == '\0') + newrev = xstrdup ("1.1"); + else if (numdots (rev) == 0) + { + newrev = (char *) xmalloc (strlen (rev) + 3); + strcpy (newrev, rev); + strcat (newrev, ".1"); + } + else + newrev = xstrdup (rev); + + /* Don't need to xstrdup NEWREV because it's already dynamic, and + not used for anything else. (Don't need to free it, either.) */ + rcs->head = newrev; + delta->version = xstrdup (newrev); + nodep = getnode(); + nodep->type = RCSVERS; + nodep->delproc = rcsvers_delproc; + nodep->data = delta; + nodep->key = delta->version; + (void) addnode (rcs->versions, nodep); + + dtext->version = xstrdup (newrev); + bufsize = 0; +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms && !S_ISREG (sb.st_mode)) + /* Pretend file is empty. */ + bufsize = 0; + else +#endif + get_file (workfile, workfile, + rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", + &dtext->text, &bufsize, &dtext->len); + + if (!(checkin_quiet || really_quiet)) + { + cvs_output ("initial revision: ", 0); + cvs_output (rcs->head, 0); + cvs_output ("\n", 1); + } + + /* We are probably about to invalidate any cached file. */ + rcsbuf_cache_close (); + + fout = rcs_internal_lockfile (rcs->path); + RCS_putadmin (rcs, fout); + RCS_putdtree (rcs, rcs->head, fout); + RCS_putdesc (rcs, fout); + rcs->delta_pos = ftello (fout); + if (rcs->delta_pos == -1) + error (1, errno, "cannot ftello for %s", rcs->path); + putdeltatext (fout, dtext); + rcs_internal_unlockfile (fout, rcs->path); + + if ((flags & RCS_FLAGS_KEEPFILE) == 0) + { + if (unlink_file (workfile) < 0) + /* FIXME-update-dir: message does not include update_dir. */ + error (0, errno, "cannot remove %s", workfile); + } + + status = 0; + goto checkin_done; + } + + /* Derive a new revision number. From the `ci' man page: + + "If rev is a revision number, it must be higher than the + latest one on the branch to which rev belongs, or must + start a new branch. + + If rev is a branch rather than a revision number, the new + revision is appended to that branch. The level number is + obtained by incrementing the tip revision number of that + branch. If rev indicates a non-existing branch, that + branch is created with the initial revision numbered + rev.1." + + RCS_findlock_or_tip handles the case where REV is omitted. + RCS 5.7 also permits REV to be "$" or to begin with a dot, but + we do not address those cases -- every routine that calls + RCS_checkin passes it a numeric revision. */ + + if (rev == NULL || *rev == '\0') + { + /* Figure out where the commit point is by looking for locks. + If the commit point is at the tip of a branch (or is the + head of the delta tree), then increment its revision number + to obtain the new revnum. Otherwise, start a new + branch. */ + commitpt = RCS_findlock_or_tip (rcs); + if (commitpt == NULL) + { + status = 1; + goto checkin_done; + } + else if (commitpt->next == NULL + || STREQ (commitpt->version, rcs->head)) + delta->version = increment_revnum (commitpt->version); + else + delta->version = RCS_addbranch (rcs, commitpt->version); + } + else + { + /* REV is either a revision number or a branch number. Find the + tip of the target branch. */ + char *branch, *tip, *newrev, *p; + int dots, isrevnum; + + assert (isdigit ((unsigned char) *rev)); + + newrev = xstrdup (rev); + dots = numdots (newrev); + isrevnum = dots & 1; + + branch = xstrdup (rev); + if (isrevnum) + { + p = strrchr (branch, '.'); + *p = '\0'; + } + + /* Find the tip of the target branch. If we got a one- or two-digit + revision number, this will be the head of the tree. Exception: + if rev is a single-field revision equal to the branch number of + the trunk (usually "1") then we want to treat it like an ordinary + branch revision. */ + if (dots == 0) + { + tip = xstrdup (rcs->head); + if (atoi (tip) != atoi (branch)) + { + newrev = (char *) xrealloc (newrev, strlen (newrev) + 3); + strcat (newrev, ".1"); + dots = isrevnum = 1; + } + } + else if (dots == 1) + tip = xstrdup (rcs->head); + else + tip = RCS_getbranch (rcs, branch, 1); + + /* If the branch does not exist, and we were supplied an exact + revision number, signal an error. Otherwise, if we were + given only a branch number, create it and set COMMITPT to + the branch point. */ + if (tip == NULL) + { + if (isrevnum) + { + error (0, 0, "%s: can't find branch point %s", + rcs->path, branch); + free (branch); + free (newrev); + status = 1; + goto checkin_done; + } + delta->version = RCS_addbranch (rcs, branch); + if (!delta->version) + { + free (branch); + free (newrev); + status = 1; + goto checkin_done; + } + adding_branch = 1; + p = strrchr (branch, '.'); + *p = '\0'; + tip = xstrdup (branch); + } + else + { + if (isrevnum) + { + /* NEWREV must be higher than TIP. */ + if (compare_revnums (tip, newrev) >= 0) + { + error (0, 0, + "%s: revision %s too low; must be higher than %s", + rcs->path, + newrev, tip); + free (branch); + free (newrev); + free (tip); + status = 1; + goto checkin_done; + } + delta->version = xstrdup (newrev); + } + else + /* Just increment the tip number to get the new revision. */ + delta->version = increment_revnum (tip); + } + + nodep = findnode (rcs->versions, tip); + commitpt = nodep->data; + + free (branch); + free (newrev); + free (tip); + } + + assert (delta->version != NULL); + + /* If COMMITPT is locked by us, break the lock. If it's locked + by someone else, signal an error. */ + nodep = findnode (RCS_getlocks (rcs), commitpt->version); + if (nodep != NULL) + { + if (! STREQ (nodep->data, delta->author)) + { + /* If we are adding a branch, then leave the old lock around. + That is sensible in the sense that when adding a branch, + we don't need to use the lock to tell us where to check + in. It is fishy in the sense that if it is our own lock, + we break it. However, this is the RCS 5.7 behavior (at + the end of addbranch in ci.c in RCS 5.7, it calls + removelock only if it is our own lock, not someone + else's). */ + + if (!adding_branch) + { + error (0, 0, "%s: revision %s locked by %s", + rcs->path, + nodep->key, (char *)nodep->data); + status = 1; + goto checkin_done; + } + } + else + delnode (nodep); + } + + dtext->version = xstrdup (delta->version); + + /* Obtain the change text for the new delta. If DELTA is to be the + new head of the tree, then its change text should be the contents + of the working file, and LEAFNODE's change text should be a diff. + Else, DELTA's change text should be a diff between LEAFNODE and + the working file. */ + + tmpfile = cvs_temp_name(); + status = RCS_checkout (rcs, NULL, commitpt->version, NULL, + ((rcs->expand != NULL + && STREQ (rcs->expand, "b")) + ? "-kb" + : "-ko"), + tmpfile, + (RCSCHECKOUTPROC)0, NULL); + if (status != 0) + error (1, 0, + "could not check out revision %s of `%s'", + commitpt->version, rcs->path); + + bufsize = 0; + changefile = cvs_temp_name(); + + /* Diff options should include --binary if the RCS file has -kb set + in its `expand' field. */ + diffopts = (rcs->expand != NULL && STREQ (rcs->expand, "b") + ? "-a -n --binary" + : "-a -n"); + + if (STREQ (commitpt->version, rcs->head) && + numdots (delta->version) == 1) + { + /* If this revision is being inserted on the trunk, the change text + for the new delta should be the contents of the working file ... */ + bufsize = 0; +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms && !S_ISREG (sb.st_mode)) + /* Pretend file is empty. */ + ; + else +#endif + get_file (workfile, workfile, + rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", + &dtext->text, &bufsize, &dtext->len); + + /* ... and the change text for the old delta should be a diff. */ + commitpt->text = (Deltatext *) xmalloc (sizeof (Deltatext)); + memset (commitpt->text, 0, sizeof (Deltatext)); + + bufsize = 0; + switch (diff_exec (workfile, tmpfile, NULL, NULL, diffopts, changefile)) + { + case 0: + case 1: + break; + case -1: + /* FIXME-update-dir: message does not include update_dir. */ + error (1, errno, "error diffing %s", workfile); + break; + default: + /* FIXME-update-dir: message does not include update_dir. */ + error (1, 0, "error diffing %s", workfile); + break; + } + + /* OK, the text file case here is really dumb. Logically + speaking we want diff to read the files in text mode, + convert them to the canonical form found in RCS files + (which, we hope at least, is independent of OS--always + bare linefeeds), and then work with change texts in that + format. However, diff_exec both generates change + texts and produces output for user purposes (e.g. patch.c), + and there is no way to distinguish between the two cases. + So we actually implement the text file case by writing the + change text as a text file, then reading it as a text file. + This should cause no harm, but doesn't strike me as + immensely clean. */ + get_file (changefile, changefile, + rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", + &commitpt->text->text, &bufsize, &commitpt->text->len); + + /* If COMMITPT->TEXT->TEXT is NULL, it means that CHANGEFILE + was empty and that there are no differences between revisions. + In that event, we want to force RCS_rewrite to write an empty + string for COMMITPT's change text. Leaving the change text + field set NULL won't work, since that means "preserve the original + change text for this delta." */ + if (commitpt->text->text == NULL) + { + commitpt->text->text = xstrdup (""); + commitpt->text->len = 0; + } + } + else + { + /* This file is not being inserted at the head, but on a side + branch somewhere. Make a diff from the previous revision + to the working file. */ + switch (diff_exec (tmpfile, workfile, NULL, NULL, diffopts, changefile)) + { + case 0: + case 1: + break; + case -1: + /* FIXME-update-dir: message does not include update_dir. */ + error (1, errno, "error diffing %s", workfile); + break; + default: + /* FIXME-update-dir: message does not include update_dir. */ + error (1, 0, "error diffing %s", workfile); + break; + } + /* See the comment above, at the other get_file invocation, + regarding binary vs. text. */ + get_file (changefile, changefile, + rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", + &dtext->text, &bufsize, + &dtext->len); + if (dtext->text == NULL) + { + dtext->text = xstrdup (""); + dtext->len = 0; + } + } + + /* Update DELTA linkage. It is important not to do this before + the very end of RCS_checkin; if an error arises that forces + us to abort checking in, we must not have malformed deltas + partially linked into the tree. + + If DELTA and COMMITPT are on different branches, do nothing -- + DELTA is linked to the tree through COMMITPT->BRANCHES, and we + don't want to change `next' pointers. + + Otherwise, if the nodes are both on the trunk, link DELTA to + COMMITPT; otherwise, link COMMITPT to DELTA. */ + + if (numdots (commitpt->version) == numdots (delta->version)) + { + if (STREQ (commitpt->version, rcs->head)) + { + delta->next = rcs->head; + rcs->head = xstrdup (delta->version); + } + else + commitpt->next = xstrdup (delta->version); + } + + /* Add DELTA to RCS->VERSIONS. */ + if (rcs->versions == NULL) + rcs->versions = getlist(); + nodep = getnode(); + nodep->type = RCSVERS; + nodep->delproc = rcsvers_delproc; + nodep->data = delta; + nodep->key = delta->version; + (void) addnode (rcs->versions, nodep); + + /* Write the new RCS file, inserting the new delta at COMMITPT. */ + if (!(checkin_quiet || really_quiet)) + { + cvs_output ("new revision: ", 14); + cvs_output (delta->version, 0); + cvs_output ("; previous revision: ", 21); + cvs_output (commitpt->version, 0); + cvs_output ("\n", 1); + } + + RCS_rewrite (rcs, dtext, commitpt->version); + + if ((flags & RCS_FLAGS_KEEPFILE) == 0) + { + if (unlink_file (workfile) < 0) + /* FIXME-update-dir: message does not include update_dir. */ + error (1, errno, "cannot remove %s", workfile); + } + if (unlink_file (tmpfile) < 0) + error (0, errno, "cannot remove %s", tmpfile); + free (tmpfile); + if (unlink_file (changefile) < 0) + error (0, errno, "cannot remove %s", changefile); + free (changefile); + + checkin_done: + free (workfile); + + if (commitpt != NULL && commitpt->text != NULL) + { + freedeltatext (commitpt->text); + commitpt->text = NULL; + } + + freedeltatext (dtext); + if (status != 0) + free_rcsvers_contents (delta); + + return status; +} + + + +/* This structure is passed between RCS_cmp_file and cmp_file_buffer. */ +struct cmp_file_data +{ + const char *filename; + FILE *fp; + int different; +}; + +/* Compare the contents of revision REV1 of RCS file RCS with the + contents of REV2 if given, otherwise, compare with the contents of + the file FILENAME. OPTIONS is a string for the keyword + expansion options. Return 0 if the contents of the revision are + the same as the contents of the file, 1 if they are different. */ +int +RCS_cmp_file (RCSNode *rcs, const char *rev1, char **rev1_cache, + const char *rev2, const char *options, const char *filename) +{ + int binary; + + TRACE (TRACE_FUNCTION, "RCS_cmp_file( %s, %s, %s, %s, %s )", + rcs->path ? rcs->path : "(null)", + rev1 ? rev1 : "(null)", rev2 ? rev2 : "(null)", + options ? options : "(null)", filename ? filename : "(null)"); + + if (options != NULL && options[0] != '\0') + binary = STREQ (options, "-kb"); + else + { + char *expand; + + expand = RCS_getexpand (rcs); + if (expand != NULL && STREQ (expand, "b")) + binary = 1; + else + binary = 0; + } + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If CVS is to deal properly with special files (when + PreservePermissions is on), the best way is to check out the + revision to a temporary file and call `xcmp' on the two disk + files. xcmp needs to handle non-regular files properly anyway, + so calling it simplifies RCS_cmp_file. We *could* just yank + the delta node out of the version tree and look for device + numbers, but writing to disk and calling xcmp is a better + abstraction (therefore probably more robust). -twp */ + + if (preserve_perms) + { + char *tmp; + int retcode; + + tmp = cvs_temp_name(); + retcode = RCS_checkout(rcs, NULL, rev, NULL, options, tmp, NULL, NULL); + if (retcode != 0) + return 1; + + retcode = xcmp (tmp, filename); + if (CVS_UNLINK (tmp) < 0) + error (0, errno, "cannot remove %s", tmp); + free (tmp); + return retcode; + } + else +#endif + { + FILE *fp; + struct cmp_file_data data; + const char *use_file1; + char *tmpfile = NULL; + + if (rev2 != NULL) + { + /* Open & cache rev1 */ + tmpfile = cvs_temp_name(); + if (RCS_checkout (rcs, NULL, rev1, NULL, options, tmpfile, + (RCSCHECKOUTPROC)0, NULL)) + error (1, errno, + "cannot check out revision %s of %s", + rev1, rcs->path); + use_file1 = tmpfile; + if (rev1_cache != NULL) + *rev1_cache = tmpfile; + } + else + use_file1 = filename; + + fp = CVS_FOPEN (use_file1, binary ? FOPEN_BINARY_READ : "r"); + if (fp == NULL) + /* FIXME-update-dir: should include update_dir in message. */ + error (1, errno, "cannot open file %s for comparing", use_file1); + + data.filename = use_file1; + data.fp = fp; + data.different = 0; + + if (RCS_checkout (rcs, NULL, rev2 ? rev2 : rev1, NULL, options, + RUN_TTY, cmp_file_buffer, &data )) + error (1, errno, + "cannot check out revision %s of %s", + rev2 ? rev2 : rev1, rcs->path); + + /* If we have not yet found a difference, make sure that we are at + the end of the file. */ + if (!data.different) + { + if (getc (fp) != EOF) + data.different = 1; + } + + fclose (fp); + if (rev1_cache == NULL && tmpfile) + { + if (CVS_UNLINK (tmpfile ) < 0) + error (0, errno, "cannot remove %s", tmpfile); + free (tmpfile); + } + + return data.different; + } +} + + + +/* This is a subroutine of RCS_cmp_file. It is passed to + RCS_checkout. */ +#define CMP_BUF_SIZE (8 * 1024) + +static void +cmp_file_buffer (void *callerdat, const char *buffer, size_t len) +{ + struct cmp_file_data *data = callerdat; + char *filebuf; + + /* If we've already found a difference, we don't need to check + further. */ + if (data->different) + return; + + filebuf = xmalloc (len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len); + + while (len > 0) + { + size_t checklen; + + checklen = len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len; + if (fread (filebuf, 1, checklen, data->fp) != checklen) + { + if (ferror (data->fp)) + error (1, errno, "cannot read file %s for comparing", + data->filename); + data->different = 1; + free (filebuf); + return; + } + + if (memcmp (filebuf, buffer, checklen) != 0) + { + data->different = 1; + free (filebuf); + return; + } + + buffer += checklen; + len -= checklen; + } + + free (filebuf); +} + + + +/* For RCS file RCS, 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 (RCSNode *rcs, const char *tag, const char *rev) +{ + List *symbols; + Node *node; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL, NULL); + + /* FIXME: This check should be moved to RCS_check_tag. There is no + reason for it to be here. */ + if (STREQ (tag, TAG_BASE) + || STREQ (tag, TAG_HEAD)) + { + /* 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; + } + + /* A revision number of NULL means use the head or default branch. + If rev is not NULL, it may be a symbolic tag or branch number; + expand it to the correct numeric revision or branch head. */ + if (rev == NULL) + rev = rcs->branch ? rcs->branch : rcs->head; + + /* At this point rcs->symbol_data may not have been parsed. + Calling RCS_symbols will force it to be parsed into a list + which we can easily manipulate. */ + symbols = RCS_symbols (rcs); + if (symbols == NULL) + { + symbols = getlist (); + rcs->symbols = symbols; + } + node = findnode (symbols, tag); + if (node != NULL) + { + free (node->data); + node->data = xstrdup (rev); + } + else + { + node = getnode (); + node->key = xstrdup (tag); + node->data = xstrdup (rev); + (void)addnode_at_front (symbols, node); + } + + return 0; +} + + + +/* Delete the symbolic tag TAG from the RCS file RCS. Return 0 if + the tag was found (and removed), or 1 if it was not present. (In + either case, the tag will no longer be in RCS->SYMBOLS.) */ + +int +RCS_deltag (RCSNode *rcs, const char *tag) +{ + List *symbols; + Node *node; + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL, NULL); + + symbols = RCS_symbols (rcs); + if (symbols == NULL) + return 1; + + node = findnode (symbols, tag); + if (node == NULL) + return 1; + + delnode (node); + + return 0; +} + + + +/* Set the default branch of RCS to REV. */ +int +RCS_setbranch (RCSNode *rcs, const char *rev) +{ + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL, NULL); + + if (rev && ! *rev) + rev = NULL; + + if (rev == NULL && rcs->branch == NULL) + return 0; + if (rev != NULL && rcs->branch != NULL && STREQ (rev, rcs->branch)) + return 0; + + if (rcs->branch != NULL) + free (rcs->branch); + rcs->branch = xstrdup (rev); + + return 0; +} + + + +/* Lock revision REV. LOCK_QUIET is 1 to suppress output. FIXME: + Most of the callers only call us because RCS_checkin still tends to + like a lock (a relic of old behavior inherited from the RCS ci + program). If we clean this up, only "cvs admin -l" will still need + to call RCS_lock. */ + +/* FIXME-twp: if a lock owned by someone else is broken, should this + send mail to the lock owner? Prompt user? It seems like such an + obscure situation for CVS as almost not worth worrying much + about. */ +int +RCS_lock (RCSNode *rcs, const char *rev, int lock_quiet) +{ + List *locks; + Node *p; + char *user; + char *xrev = NULL; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL, NULL); + + locks = RCS_getlocks (rcs); + if (locks == NULL) + locks = rcs->locks = getlist(); + user = getcaller(); + + /* A revision number of NULL means lock the head or default branch. */ + if (rev == NULL) + xrev = RCS_head (rcs); + else + xrev = RCS_gettag (rcs, rev, 1, NULL); + + /* Make sure that the desired revision exists. Technically, + we can update the locks list without even checking this, + but RCS 5.7 did this. And it can't hurt. */ + if (xrev == NULL || findnode (rcs->versions, xrev) == NULL) + { + if (!lock_quiet) + error (0, 0, "%s: revision %s absent", rcs->path, rev); + free (xrev); + return 1; + } + + /* Is this rev already locked? */ + p = findnode (locks, xrev); + if (p != NULL) + { + if (STREQ (p->data, user)) + { + /* We already own the lock on this revision, so do nothing. */ + free (xrev); + return 0; + } + +#if 0 + /* Well, first of all, "rev" below should be "xrev" to avoid + core dumps. But more importantly, should we really be + breaking the lock unconditionally? What CVS 1.9 does (via + RCS) is to prompt "Revision 1.1 is already locked by fred. + Do you want to break the lock? [ny](n): ". Well, we don't + want to interact with the user (certainly not at the + server/protocol level, and probably not in the command-line + client), but isn't it more sensible to give an error and + let the user run "cvs admin -u" if they want to break the + lock? */ + + /* Break the lock. */ + if (!lock_quiet) + { + cvs_output (rev, 0); + cvs_output (" unlocked\n", 0); + } + delnode (p); +#else + error (1, 0, "Revision %s is already locked by %s", + xrev, (char *)p->data); +#endif + } + + /* Create a new lock. */ + p = getnode(); + p->key = xrev; /* already xstrdupped */ + p->data = xstrdup (getcaller()); + (void)addnode_at_front (locks, p); + + if (!lock_quiet) + { + cvs_output (xrev, 0); + cvs_output (" locked\n", 0); + } + + return 0; +} + + + +/* Unlock revision REV. UNLOCK_QUIET is 1 to suppress output. FIXME: + Like RCS_lock, this can become a no-op if we do the checkin + ourselves. + + If REV is not null and is locked by someone else, break their + lock and notify them. It is an open issue whether RCS_unlock + queries the user about whether or not to break the lock. */ +int +RCS_unlock (RCSNode *rcs, char *rev, int unlock_quiet) +{ + Node *lock; + List *locks; + char *user; + char *xrev = NULL; + + user = getcaller(); + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL, NULL); + + /* If rev is NULL, unlock the revision held by the caller; if more + than one, make the user specify the revision explicitly. This + differs from RCS which unlocks the latest revision (first in + rcs->locks) held by the caller. */ + if (rev == NULL) + { + Node *p; + + /* No-ops: attempts to unlock an empty tree or an unlocked file. */ + if (rcs->head == NULL) + { + if (!unlock_quiet) + cvs_outerr ("can't unlock an empty tree\n", 0); + return 0; + } + + locks = RCS_getlocks (rcs); + if (locks == NULL) + { + if (!unlock_quiet) + cvs_outerr ("No locks are set.\n", 0); + return 0; + } + + lock = NULL; + for (p = locks->list->next; p != locks->list; p = p->next) + { + if (STREQ (p->data, user)) + { + if (lock != NULL) + { + if (!unlock_quiet) + error (0, 0, "\ +%s: multiple revisions locked by %s; please specify one", rcs->path, user); + return 1; + } + lock = p; + } + } + if (lock == NULL) + { + if (!unlock_quiet) + error (0, 0, "No locks are set for %s.\n", user); + return 0; /* no lock found, ergo nothing to do */ + } + xrev = xstrdup (lock->key); + } + else + { + xrev = RCS_gettag (rcs, rev, 1, (int *) NULL); + if (xrev == NULL) + { + error (0, 0, "%s: revision %s absent", rcs->path, rev); + return 1; + } + } + + lock = findnode (RCS_getlocks (rcs), xrev); + if (lock == NULL) + { + /* This revision isn't locked. */ + free (xrev); + return 0; + } + + if (! STREQ (lock->data, user)) + { + /* If the revision is locked by someone else, notify + them. Note that this shouldn't ever happen if RCS_unlock + is called with a NULL revision, since that means "whatever + revision is currently locked by the caller." */ + char *repos, *workfile; + if (!unlock_quiet) + error (0, 0, "\ +%s: revision %s locked by %s; breaking lock", rcs->path, xrev, (char *)lock->data); + repos = xstrdup (rcs->path); + workfile = strrchr (repos, '/'); + *workfile++ = '\0'; + notify_do ('C', workfile, user, NULL, NULL, repos); + free (repos); + } + + delnode (lock); + if (!unlock_quiet) + { + cvs_output (xrev, 0); + cvs_output (" unlocked\n", 0); + } + + free (xrev); + return 0; +} + + + +/* Add USER to the access list of RCS. Do nothing if already present. + FIXME-twp: check syntax of USER to make sure it's a valid id. */ + +void +RCS_addaccess (RCSNode *rcs, char *user) +{ + char *access, *a; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL, NULL); + + if (rcs->access == NULL) + rcs->access = xstrdup (user); + else + { + access = xstrdup (rcs->access); + for (a = strtok (access, " "); a != NULL; a = strtok (NULL, " ")) + { + if (STREQ (a, user)) + { + free (access); + return; + } + } + free (access); + rcs->access = (char *) xrealloc + (rcs->access, strlen (rcs->access) + strlen (user) + 2); + strcat (rcs->access, " "); + strcat (rcs->access, user); + } +} + + + +/* Remove USER from the access list of RCS. */ +void +RCS_delaccess (RCSNode *rcs, char *user) +{ + char *p, *s; + int ulen; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL, NULL); + + if (rcs->access == NULL) + return; + + if (user == NULL) + { + free (rcs->access); + rcs->access = NULL; + return; + } + + p = rcs->access; + ulen = strlen (user); + while (p != NULL) + { + if (strncmp (p, user, ulen) == 0 && (p[ulen] == '\0' || p[ulen] == ' ')) + break; + p = strchr (p, ' '); + if (p != NULL) + ++p; + } + + if (p == NULL) + return; + + s = p + ulen; + while (*s != '\0') + *p++ = *s++; + *p = '\0'; +} + + + +char * +RCS_getaccess (RCSNode *rcs) +{ + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + return rcs->access; +} + + + +/* Return a nonzero value if the revision specified by ARG is found. */ +static int +findtag (Node *node, void *arg) +{ + char *rev = arg; + + if (STREQ (node->data, rev)) + return 1; + else + return 0; +} + + + +/* Delete revisions between REV1 and REV2. The changes between the two + revisions must be collapsed, and the result stored in the revision + immediately preceding the lower one. Return 0 for successful completion, + 1 otherwise. + + Solution: check out the revision preceding REV1 and the revision + following REV2. Use call_diff to find aggregate diffs between + these two revisions, and replace the delta text for the latter one + with the new aggregate diff. Alternatively, we could write a + function that takes two change texts and combines them to produce a + new change text, without checking out any revs or calling diff. It + would be hairy, but so, so cool. + + If INCLUSIVE is set, then TAG1 and TAG2, if non-NULL, tell us to + delete that revision as well (cvs admin -o tag1:tag2). If clear, + delete up to but not including that revision (cvs admin -o tag1::tag2). + This does not affect TAG1 or TAG2 being NULL; the meaning of the start + point in ::tag2 and :tag2 is the same and likewise for end points. */ +int +RCS_delete_revs (RCSNode *rcs, char *tag1, char *tag2, int inclusive) +{ + char *next; + Node *nodep; + RCSVers *revp = NULL; + RCSVers *beforep; + int status, found; + int save_noexec; + + char *branchpoint = NULL; + char *rev1 = NULL; + char *rev2 = NULL; + int rev1_inclusive = inclusive; + int rev2_inclusive = inclusive; + char *before = NULL; + char *after = NULL; + char *beforefile = NULL; + char *afterfile = NULL; + char *outfile = NULL; + + if (tag1 == NULL && tag2 == NULL) + return 0; + + /* Assume error status until everything is finished. */ + status = 1; + + /* Make sure both revisions exist. */ + if (tag1 != NULL) + { + rev1 = RCS_gettag (rcs, tag1, 1, NULL); + if (rev1 == NULL || (nodep = findnode (rcs->versions, rev1)) == NULL) + { + error (0, 0, "%s: Revision %s doesn't exist.", rcs->path, tag1); + goto delrev_done; + } + } + if (tag2 != NULL) + { + rev2 = RCS_gettag (rcs, tag2, 1, NULL); + if (rev2 == NULL || (nodep = findnode (rcs->versions, rev2)) == NULL) + { + error (0, 0, "%s: Revision %s doesn't exist.", rcs->path, tag2); + goto delrev_done; + } + } + + /* If rev1 is on the trunk and rev2 is NULL, rev2 should be + RCS->HEAD. (*Not* RCS_head(rcs), which may return rcs->branch + instead.) We need to check this special case early, in order + to make sure that rev1 and rev2 get ordered correctly. */ + if (rev2 == NULL && numdots (rev1) == 1) + { + rev2 = xstrdup (rcs->head); + rev2_inclusive = 1; + } + + if (rev2 == NULL) + rev2_inclusive = 1; + + if (rev1 != NULL && rev2 != NULL) + { + /* A range consisting of a branch number means the latest revision + on that branch. */ + if (RCS_isbranch (rcs, rev1) && STREQ (rev1, rev2)) + rev1 = rev2 = RCS_getbranch (rcs, rev1, 0); + else + { + /* Make sure REV1 and REV2 are ordered correctly (in the + same order as the next field). For revisions on the + trunk, REV1 should be higher than REV2; for branches, + REV1 should be lower. */ + /* Shouldn't we just be giving an error in the case where + the user specifies the revisions in the wrong order + (that is, always swap on the trunk, never swap on a + branch, in the non-error cases)? It is not at all + clear to me that users who specify -o 1.4:1.2 really + meant to type -o 1.2:1.4, and the out of order usage + has never been documented, either by cvs.texinfo or + rcs(1). */ + char *temp; + int temp_inclusive; + if (numdots (rev1) == 1) + { + if (compare_revnums (rev1, rev2) <= 0) + { + temp = rev2; + rev2 = rev1; + rev1 = temp; + + temp_inclusive = rev2_inclusive; + rev2_inclusive = rev1_inclusive; + rev1_inclusive = temp_inclusive; + } + } + else if (compare_revnums (rev1, rev2) > 0) + { + temp = rev2; + rev2 = rev1; + rev1 = temp; + + temp_inclusive = rev2_inclusive; + rev2_inclusive = rev1_inclusive; + rev1_inclusive = temp_inclusive; + } + } + } + + /* Basically the same thing; make sure that the ordering is what we + need. */ + if (rev1 == NULL) + { + assert (rev2 != NULL); + if (numdots (rev2) == 1) + { + /* Swap rev1 and rev2. */ + int temp_inclusive; + + rev1 = rev2; + rev2 = NULL; + + temp_inclusive = rev2_inclusive; + rev2_inclusive = rev1_inclusive; + rev1_inclusive = temp_inclusive; + } + } + + /* Put the revision number preceding the first one to delete into + BEFORE (where "preceding" means according to the next field). + If the first revision to delete is the first revision on its + branch (e.g. 1.3.2.1), BEFORE should be the node on the trunk + at which the branch is rooted. If the first revision to delete + is the head revision of the trunk, set BEFORE to NULL. + + Note that because BEFORE may not be on the same branch as REV1, + it is not very handy for navigating the revision tree. It's + most useful just for checking out the revision preceding REV1. */ + before = NULL; + branchpoint = RCS_getbranchpoint (rcs, rev1 != NULL ? rev1 : rev2); + if (rev1 == NULL) + { + rev1 = xstrdup (branchpoint); + if (numdots (branchpoint) > 1) + { + char *bp; + bp = strrchr (branchpoint, '.'); + while (*--bp != '.') + ; + *bp = '\0'; + /* Note that this is exclusive, always, because the inclusive + flag doesn't affect the meaning when rev1 == NULL. */ + before = xstrdup (branchpoint); + *bp = '.'; + } + } + else if (! STREQ (rev1, branchpoint)) + { + /* Walk deltas from BRANCHPOINT on, looking for REV1. */ + nodep = findnode (rcs->versions, branchpoint); + revp = nodep->data; + while (revp->next != NULL && ! STREQ (revp->next, rev1)) + { + revp = nodep->data; + nodep = findnode (rcs->versions, revp->next); + } + if (revp->next == NULL) + { + error (0, 0, "%s: Revision %s doesn't exist.", rcs->path, rev1); + goto delrev_done; + } + if (rev1_inclusive) + before = xstrdup (revp->version); + else + { + before = rev1; + nodep = findnode (rcs->versions, before); + rev1 = xstrdup (((RCSVers *)nodep->data)->next); + } + } + else if (!rev1_inclusive) + { + before = rev1; + nodep = findnode (rcs->versions, before); + rev1 = xstrdup (((RCSVers *)nodep->data)->next); + } + else if (numdots (branchpoint) > 1) + { + /* Example: rev1 is "1.3.2.1", branchpoint is "1.3.2.1". + Set before to "1.3". */ + char *bp; + bp = strrchr (branchpoint, '.'); + while (*--bp != '.') + ; + *bp = '\0'; + before = xstrdup (branchpoint); + *bp = '.'; + } + + /* If any revision between REV1 and REV2 is locked or is a branch point, + we can't delete that revision and must abort. */ + after = NULL; + next = rev1; + found = 0; + while (!found && next != NULL) + { + nodep = findnode (rcs->versions, next); + revp = nodep->data; + + if (rev2 != NULL) + found = STREQ (revp->version, rev2); + next = revp->next; + + if ((!found && next != NULL) || rev2_inclusive || rev2 == NULL) + { + if (findnode (RCS_getlocks (rcs), revp->version)) + { + error (0, 0, "%s: can't remove locked revision %s", + rcs->path, + revp->version); + goto delrev_done; + } + if (revp->branches != NULL) + { + error (0, 0, "%s: can't remove branch point %s", + rcs->path, + revp->version); + goto delrev_done; + } + + /* Doing this only for the :: syntax is for compatibility. + See cvs.texinfo for somewhat more discussion. */ + if (!inclusive + && walklist (RCS_symbols (rcs), findtag, revp->version)) + { + /* We don't print which file this happens to on the theory + that the caller will print the name of the file in a + more useful fashion (fullname not rcs->path). */ + error (0, 0, "cannot remove revision %s because it has tags", + revp->version); + goto delrev_done; + } + + /* It's misleading to print the `deleting revision' output + here, since we may not actually delete these revisions. + But that's how RCS does it. Bleah. Someday this should be + moved to the point where the revs are actually marked for + deletion. -twp */ + cvs_output ("deleting revision ", 0); + cvs_output (revp->version, 0); + cvs_output ("\n", 1); + } + } + + if (rev2 == NULL) + ; + else if (found) + { + if (rev2_inclusive) + after = xstrdup (next); + else + after = xstrdup (revp->version); + } + else if (!inclusive) + { + /* In the case of an empty range, for example 1.2::1.2 or + 1.2::1.3, we want to just do nothing. */ + status = 0; + goto delrev_done; + } + else + { + /* This looks fishy in the cases where tag1 == NULL or tag2 == NULL. + Are those cases really impossible? */ + assert (tag1 != NULL); + assert (tag2 != NULL); + + error (0, 0, "%s: invalid revision range %s:%s", rcs->path, + tag1, tag2); + goto delrev_done; + } + + if (after == NULL && before == NULL) + { + /* The user is trying to delete all revisions. While an + RCS file without revisions makes sense to RCS (e.g. the + state after "rcs -i"), CVS has never been able to cope with + it. So at least for now we just make this an error. + + We don't include rcs->path in the message since "cvs admin" + already printed "RCS file:" and the name. */ + error (1, 0, "attempt to delete all revisions"); + } + + /* The conditionals at this point get really hairy. Here is the + general idea: + + IF before != NULL and after == NULL + THEN don't check out any revisions, just delete them + IF before == NULL and after != NULL + THEN only check out after's revision, and use it for the new deltatext + ELSE + check out both revisions and diff -n them. This could use + RCS_exec_rcsdiff with some changes, like being able + to suppress diagnostic messages and to direct output. */ + + if (after != NULL) + { + char *diffbuf; + size_t bufsize, len; + +#if defined (WOE32) && !defined (__CYGWIN32__) + /* FIXME: This is an awful kludge, but at least until I have + time to work on it a little more and test it, I'd rather + give a fatal error than corrupt the file. I think that we + need to use "-kb" and "--binary" and "rb" to get_file + (probably can do it always, not just for binary files, if + we are consistent between the RCS_checkout and the diff). */ + { + char *expand = RCS_getexpand (rcs); + if (expand != NULL && STREQ (expand, "b")) + error (1, 0, + "admin -o not implemented yet for binary on this system"); + } +#endif /* WOE32 */ + + afterfile = cvs_temp_name(); + status = RCS_checkout (rcs, NULL, after, NULL, "-ko", afterfile, + (RCSCHECKOUTPROC)0, NULL); + if (status > 0) + goto delrev_done; + + if (before == NULL) + { + /* We are deleting revisions from the head of the tree, + so must create a new head. */ + diffbuf = NULL; + bufsize = 0; + get_file (afterfile, afterfile, "r", &diffbuf, &bufsize, &len); + + save_noexec = noexec; + noexec = 0; + if (unlink_file (afterfile) < 0) + error (0, errno, "cannot remove %s", afterfile); + noexec = save_noexec; + + free (afterfile); + afterfile = NULL; + + free (rcs->head); + rcs->head = xstrdup (after); + } + else + { + beforefile = cvs_temp_name(); + status = RCS_checkout (rcs, NULL, before, NULL, "-ko", beforefile, + (RCSCHECKOUTPROC)0, NULL); + if (status > 0) + goto delrev_done; + + outfile = cvs_temp_name(); + status = diff_exec (beforefile, afterfile, NULL, NULL, "-an", + outfile); + + if (status == 2) + { + /* Not sure we need this message; will diff_exec already + have printed an error? */ + error (0, 0, "%s: could not diff", rcs->path); + status = 1; + goto delrev_done; + } + + diffbuf = NULL; + bufsize = 0; + get_file (outfile, outfile, "r", &diffbuf, &bufsize, &len); + } + + /* Save the new change text in after's delta node. */ + nodep = findnode (rcs->versions, after); + revp = nodep->data; + + assert (revp->text == NULL); + + revp->text = (Deltatext *) xmalloc (sizeof (Deltatext)); + memset ((Deltatext *) revp->text, 0, sizeof (Deltatext)); + revp->text->version = xstrdup (revp->version); + revp->text->text = diffbuf; + revp->text->len = len; + + /* If DIFFBUF is NULL, it means that OUTFILE is empty and that + there are no differences between the two revisions. In that + case, we want to force RCS_copydeltas to write an empty string + for the new change text (leaving the text field set NULL + means "preserve the original change text for this delta," so + we don't want that). */ + if (revp->text->text == NULL) + revp->text->text = xstrdup (""); + } + + /* Walk through the revisions (again) to mark each one as + outdated. (FIXME: would it be safe to use the `dead' field for + this? Doubtful.) */ + for (next = rev1; + next != NULL && (after == NULL || ! STREQ (next, after)); + next = revp->next) + { + nodep = findnode (rcs->versions, next); + revp = nodep->data; + revp->outdated = 1; + } + + /* Update delta links. If BEFORE == NULL, we're changing the + head of the tree and don't need to update any `next' links. */ + if (before != NULL) + { + /* If REV1 is the first node on its branch, then BEFORE is its + root node (on the trunk) and we have to update its branches + list. Otherwise, BEFORE is on the same branch as AFTER, and + we can just change BEFORE's `next' field to point to AFTER. + (This should be safe: since findnode manages its lists via + the `hashnext' and `hashprev' fields, rather than `next' and + `prev', mucking with `next' and `prev' should not corrupt the + delta tree's internal structure. Much. -twp) */ + + if (rev1 == NULL) + /* beforep's ->next field already should be equal to after, + which I think is always NULL in this case. */ + ; + else if (STREQ (rev1, branchpoint)) + { + nodep = findnode (rcs->versions, before); + revp = nodep->data; + nodep = revp->branches->list->next; + while (nodep != revp->branches->list && + ! STREQ (nodep->key, rev1)) + nodep = nodep->next; + assert (nodep != revp->branches->list); + if (after == NULL) + delnode (nodep); + else + { + free (nodep->key); + nodep->key = xstrdup (after); + } + } + else + { + nodep = findnode (rcs->versions, before); + beforep = nodep->data; + free (beforep->next); + beforep->next = xstrdup (after); + } + } + + status = 0; + + delrev_done: + if (rev1 != NULL) + free (rev1); + if (rev2 != NULL) + free (rev2); + if (branchpoint != NULL) + free (branchpoint); + if (before != NULL) + free (before); + if (after != NULL) + free (after); + + save_noexec = noexec; + noexec = 0; + if (beforefile != NULL) + { + if (unlink_file (beforefile) < 0) + error (0, errno, "cannot remove %s", beforefile); + free (beforefile); + } + if (afterfile != NULL) + { + if (unlink_file (afterfile) < 0) + error (0, errno, "cannot remove %s", afterfile); + free (afterfile); + } + if (outfile != NULL) + { + if (unlink_file (outfile) < 0) + error (0, errno, "cannot remove %s", outfile); + free (outfile); + } + noexec = save_noexec; + + return status; +} + + + +/* + * TRUE if there exists a symbolic tag "tag" in file. + */ +int +RCS_exist_tag (RCSNode *rcs, char *tag) +{ + + assert (rcs != NULL); + + if (findnode (RCS_symbols (rcs), tag)) + return 1; + return 0; + +} + +/* + * TRUE if RCS revision number "rev" exists. + * This includes magic branch revisions, not found in rcs->versions, + * but only in rcs->symbols, requiring a list walk to find them. + * Take advantage of list walk callback function already used by + * RCS_delete_revs, above. + */ +int +RCS_exist_rev (RCSNode *rcs, char *rev) +{ + + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + if (findnode(rcs->versions, rev) != 0) + return 1; + + if (walklist (RCS_symbols(rcs), findtag, rev) != 0) + return 1; + + return 0; + +} + + +/* RCS_deltas and friends. Processing of the deltas in RCS files. */ + +struct line +{ + /* Text of this line. Part of the same malloc'd block as the struct + line itself (we probably should use the "struct hack" (char text[1]) + and save ourselves sizeof (char *) bytes). Does not include \n; + instead has_newline indicates the presence or absence of \n. */ + char *text; + /* Length of this line, not counting \n if has_newline is true. */ + size_t len; + /* Version in which it was introduced. */ + RCSVers *vers; + /* Nonzero if this line ends with \n. This will always be true + except possibly for the last line. */ + int has_newline; + /* Number of pointers to this struct line. */ + int refcount; +}; + +struct linevector +{ + /* How many lines in use for this linevector? */ + unsigned int nlines; + /* How many lines allocated for this linevector? */ + unsigned int lines_alloced; + /* Pointer to array containing a pointer to each line. */ + struct line **vector; +}; + +static void linevector_init (struct linevector *); + +/* Initialize *VEC to be a linevector with no lines. */ +static void +linevector_init (struct linevector *vec) +{ + vec->lines_alloced = 0; + vec->nlines = 0; + vec->vector = NULL; +} + +static int linevector_add (struct linevector *vec, const char *text, + size_t len, RCSVers *vers, + unsigned int pos); + +/* Given some text TEXT, add each of its lines to VEC before line POS + (where line 0 is the first line). The last line in TEXT may or may + not be \n terminated. + Set the version for each of the new lines to VERS. This + function returns non-zero for success. It returns zero if the line + number is out of range. + + Each of the lines in TEXT are copied to space which is managed with + the linevector (and freed by linevector_free). So the caller doesn't + need to keep TEXT around after the call to this function. */ +static int +linevector_add (struct linevector *vec, const char *text, size_t len, RCSVers *vers, unsigned int pos) +{ + const char *textend; + unsigned int i; + unsigned int nnew; + const char *p; + const char *nextline_text; + size_t nextline_len; + int nextline_newline; + struct line *q; + + if (len == 0) + return 1; + + textend = text + len; + + /* Count the number of lines we will need to add. */ + nnew = 1; + for (p = text; p < textend; ++p) + if (*p == '\n' && p + 1 < textend) + ++nnew; + + /* Expand VEC->VECTOR if needed. */ + if (vec->nlines + nnew >= vec->lines_alloced) + { + if (vec->lines_alloced == 0) + vec->lines_alloced = 10; + while (vec->nlines + nnew >= vec->lines_alloced) + vec->lines_alloced *= 2; + vec->vector = xrealloc (vec->vector, + vec->lines_alloced * sizeof (*vec->vector)); + } + + /* Make room for the new lines in VEC->VECTOR. */ + for (i = vec->nlines + nnew - 1; i >= pos + nnew; --i) + vec->vector[i] = vec->vector[i - nnew]; + + if (pos > vec->nlines) + return 0; + + /* Actually add the lines, to VEC->VECTOR. */ + i = pos; + nextline_text = text; + nextline_newline = 0; + for (p = text; p < textend; ++p) + if (*p == '\n') + { + nextline_newline = 1; + if (p + 1 == textend) + /* If there are no characters beyond the last newline, we + don't consider it another line. */ + break; + nextline_len = p - nextline_text; + q = (struct line *) xmalloc (sizeof (struct line) + nextline_len); + q->vers = vers; + q->text = (char *)q + sizeof (struct line); + q->len = nextline_len; + q->has_newline = nextline_newline; + q->refcount = 1; + memcpy (q->text, nextline_text, nextline_len); + vec->vector[i++] = q; + + nextline_text = (char *)p + 1; + nextline_newline = 0; + } + nextline_len = p - nextline_text; + q = (struct line *) xmalloc (sizeof (struct line) + nextline_len); + q->vers = vers; + q->text = (char *)q + sizeof (struct line); + q->len = nextline_len; + q->has_newline = nextline_newline; + q->refcount = 1; + memcpy (q->text, nextline_text, nextline_len); + vec->vector[i] = q; + + vec->nlines += nnew; + + return 1; +} + +static void linevector_delete (struct linevector *, unsigned int, + unsigned int); + +/* Remove NLINES lines from VEC at position POS (where line 0 is the + first line). */ +static void +linevector_delete (struct linevector *vec, unsigned int pos, unsigned int nlines) +{ + unsigned int i; + unsigned int last; + + last = vec->nlines - nlines; + for (i = pos; i < pos + nlines; ++i) + { + if (--vec->vector[i]->refcount == 0) + free (vec->vector[i]); + } + for (i = pos; i < last; ++i) + vec->vector[i] = vec->vector[i + nlines]; + vec->nlines -= nlines; +} + +static void linevector_copy (struct linevector *, struct linevector *); + +/* Copy FROM to TO, copying the vectors but not the lines pointed to. */ +static void +linevector_copy (struct linevector *to, struct linevector *from) +{ + unsigned int ln; + + for (ln = 0; ln < to->nlines; ++ln) + { + if (--to->vector[ln]->refcount == 0) + free (to->vector[ln]); + } + if (from->nlines > to->lines_alloced) + { + if (to->lines_alloced == 0) + to->lines_alloced = 10; + while (from->nlines > to->lines_alloced) + to->lines_alloced *= 2; + to->vector = (struct line **) + xrealloc (to->vector, to->lines_alloced * sizeof (*to->vector)); + } + memcpy (to->vector, from->vector, + from->nlines * sizeof (*to->vector)); + to->nlines = from->nlines; + for (ln = 0; ln < to->nlines; ++ln) + ++to->vector[ln]->refcount; +} + +static void linevector_free (struct linevector *); + +/* Free storage associated with linevector. */ +static void +linevector_free (struct linevector *vec) +{ + unsigned int ln; + + if (vec->vector != NULL) + { + for (ln = 0; ln < vec->nlines; ++ln) + if (--vec->vector[ln]->refcount == 0) + free (vec->vector[ln]); + + free (vec->vector); + } +} + +static char *month_printname (char *); + +/* Given a textual string giving the month (1-12), terminated with any + character not recognized by atoi, return the 3 character name to + print it with. I do not think it is a good idea to change these + strings based on the locale; they are standard abbreviations (for + example in rfc822 mail messages) which should be widely understood. + Returns a pointer into static readonly storage. */ +static char * +month_printname (char *month) +{ + static const char *const months[] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + int mnum; + + mnum = atoi (month); + if (mnum < 1 || mnum > 12) + return "???"; + return (char *)months[mnum - 1]; +} + +static int +apply_rcs_changes (struct linevector *, const char *, size_t, + const char *, RCSVers *, RCSVers *); + +/* Apply changes to the line vector LINES. DIFFBUF is a buffer of + length DIFFLEN holding the change text from an RCS file (the output + of diff -n). NAME is used in error messages. The VERS field of + any line added is set to ADDVERS. The VERS field of any line + deleted is set to DELVERS, unless DELVERS is NULL, in which case + the VERS field of deleted lines is unchanged. The function returns + non-zero if the change text is applied successfully. It returns + zero if the change text does not appear to apply to LINES (e.g., a + line number is invalid). If the change text is improperly + formatted (e.g., it is not the output of diff -n), the function + calls error with a status of 1, causing the program to exit. */ + +static int +apply_rcs_changes (struct linevector *lines, const char *diffbuf, size_t difflen, const char *name, RCSVers *addvers, RCSVers *delvers) +{ + const char *p; + const char *q; + int op; + /* The RCS format throws us for a loop in that the deltafrags (if + we define a deltafrag as an add or a delete) need to be applied + in reverse order. So we stick them into a linked list. */ + struct deltafrag { + enum {FRAG_ADD, FRAG_DELETE} type; + unsigned long pos; + unsigned long nlines; + const char *new_lines; + size_t len; + struct deltafrag *next; + }; + struct deltafrag *dfhead; + struct deltafrag *df; + + dfhead = NULL; + for (p = diffbuf; p != NULL && p < diffbuf + difflen; ) + { + op = *p++; + if (op != 'a' && op != 'd') + /* Can't just skip over the deltafrag, because the value + of op determines the syntax. */ + error (1, 0, "unrecognized operation '\\x%x' in %s", + op, name); + df = (struct deltafrag *) xmalloc (sizeof (struct deltafrag)); + df->next = dfhead; + dfhead = df; + df->pos = strtoul (p, (char **) &q, 10); + + if (p == q) + error (1, 0, "number expected in %s", name); + p = q; + if (*p++ != ' ') + error (1, 0, "space expected in %s", name); + df->nlines = strtoul (p, (char **) &q, 10); + if (p == q) + error (1, 0, "number expected in %s", name); + p = q; + if (*p++ != '\012') + error (1, 0, "linefeed expected in %s", name); + + if (op == 'a') + { + unsigned int i; + + df->type = FRAG_ADD; + i = df->nlines; + /* The text we want is the number of lines specified, or + until the end of the value, whichever comes first (it + will be the former except in the case where we are + adding a line which does not end in newline). */ + for (q = p; i != 0; ++q) + if (*q == '\n') + --i; + else if (q == diffbuf + difflen) + { + if (i != 1) + error (1, 0, "premature end of change in %s", name); + else + break; + } + + /* Stash away a pointer to the text we are adding. */ + df->new_lines = p; + df->len = q - p; + + p = q; + } + else + { + /* Correct for the fact that line numbers in RCS files + start with 1. */ + --df->pos; + + assert (op == 'd'); + df->type = FRAG_DELETE; + } + } + + for (df = dfhead; df != NULL;) + { + unsigned int ln; + + switch (df->type) + { + case FRAG_ADD: + if (! linevector_add (lines, df->new_lines, df->len, addvers, + df->pos)) + return 0; + break; + case FRAG_DELETE: + if (df->pos > lines->nlines + || df->pos + df->nlines > lines->nlines) + return 0; + if (delvers != NULL) + for (ln = df->pos; ln < df->pos + df->nlines; ++ln) + lines->vector[ln]->vers = delvers; + linevector_delete (lines, df->pos, df->nlines); + break; + } + df = df->next; + free (dfhead); + dfhead = df; + } + + return 1; +} + +/* Apply an RCS change text to a buffer. The function name starts + with rcs rather than RCS because this does not take an RCSNode + argument. NAME is used in error messages. TEXTBUF is the text + buffer to change, and TEXTLEN is the size. DIFFBUF and DIFFLEN are + the change buffer and size. The new buffer is returned in *RETBUF + and *RETLEN. The new buffer is allocated by xmalloc. + + Return 1 for success. On failure, call error and return 0. */ + +int +rcs_change_text (const char *name, char *textbuf, size_t textlen, const char *diffbuf, size_t difflen, char **retbuf, size_t *retlen) +{ + struct linevector lines; + int ret; + + *retbuf = NULL; + *retlen = 0; + + linevector_init (&lines); + + if (! linevector_add (&lines, textbuf, textlen, NULL, 0)) + error (1, 0, "cannot initialize line vector"); + + if (! apply_rcs_changes (&lines, diffbuf, difflen, name, NULL, NULL)) + { + error (0, 0, "invalid change text in %s", name); + ret = 0; + } + else + { + char *p; + size_t n; + unsigned int ln; + + n = 0; + for (ln = 0; ln < lines.nlines; ++ln) + /* 1 for \n */ + n += lines.vector[ln]->len + 1; + + p = xmalloc (n); + *retbuf = p; + + for (ln = 0; ln < lines.nlines; ++ln) + { + memcpy (p, lines.vector[ln]->text, lines.vector[ln]->len); + p += lines.vector[ln]->len; + if (lines.vector[ln]->has_newline) + *p++ = '\n'; + } + + *retlen = p - *retbuf; + assert (*retlen <= n); + + ret = 1; + } + + linevector_free (&lines); + + return ret; +} + +/* Walk the deltas in RCS to get to revision VERSION. + + If OP is RCS_ANNOTATE, then write annotations using cvs_output. + + If OP is RCS_FETCH, then put the contents of VERSION into a + newly-malloc'd array and put a pointer to it in *TEXT. Each line + is \n terminated; the caller is responsible for converting text + files if desired. The total length is put in *LEN. + + If FP is non-NULL, it should be a file descriptor open to the file + RCS with file position pointing to the deltas. We close the file + when we are done. + + If LOG is non-NULL, then *LOG is set to the log message of VERSION, + and *LOGLEN is set to the length of the log message. + + On error, give a fatal error. */ + +void +RCS_deltas (RCSNode *rcs, FILE *fp, struct rcsbuffer *rcsbuf, + const char *version, enum rcs_delta_op op, char **text, + size_t *len, char **log, size_t *loglen) +{ + struct rcsbuffer rcsbuf_local; + char *branchversion; + char *cpversion; + char *key; + char *value; + size_t vallen; + RCSVers *vers; + RCSVers *prev_vers; + RCSVers *trunk_vers; + char *next; + int ishead, isnext, isversion, onbranch; + Node *node; + struct linevector headlines; + struct linevector curlines; + struct linevector trunklines; + int foundhead; + + if (fp == NULL) + { + rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf_local); + rcsbuf = &rcsbuf_local; + } + + ishead = 1; + vers = NULL; + prev_vers = NULL; + trunk_vers = NULL; + next = NULL; + onbranch = 0; + foundhead = 0; + + linevector_init (&curlines); + linevector_init (&headlines); + linevector_init (&trunklines); + + /* We set BRANCHVERSION to the version we are currently looking + for. Initially, this is the version on the trunk from which + VERSION branches off. If VERSION is not a branch, then + BRANCHVERSION is just VERSION. */ + branchversion = xstrdup (version); + cpversion = strchr (branchversion, '.'); + if (cpversion != NULL) + cpversion = strchr (cpversion + 1, '.'); + if (cpversion != NULL) + *cpversion = '\0'; + + do { + if (! rcsbuf_getrevnum (rcsbuf, &key)) + error (1, 0, "unexpected EOF reading RCS file %s", rcs->path); + + if (next != NULL && ! STREQ (next, key)) + { + /* This is not the next version we need. It is a branch + version which we want to ignore. */ + isnext = 0; + isversion = 0; + } + else + { + isnext = 1; + + /* look up the revision */ + node = findnode (rcs->versions, key); + if (node == NULL) + error (1, 0, + "mismatch in rcs file %s between deltas and deltatexts (%s)", + rcs->path, key); + + /* Stash the previous version. */ + prev_vers = vers; + + vers = node->data; + next = vers->next; + + /* Compare key and trunkversion now, because key points to + storage controlled by rcsbuf_getkey. */ + if (STREQ (branchversion, key)) + isversion = 1; + else + isversion = 0; + } + + while (1) + { + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "%s does not appear to be a valid rcs file", + rcs->path); + + if (log != NULL + && isversion + && STREQ (key, "log") + && STREQ (branchversion, version)) + { + *log = rcsbuf_valcopy (rcsbuf, value, 0, loglen); + } + + if (STREQ (key, "text")) + { + rcsbuf_valpolish (rcsbuf, value, 0, &vallen); + if (ishead) + { + if (! linevector_add (&curlines, value, vallen, NULL, 0)) + error (1, 0, "invalid rcs file %s", rcs->path); + + ishead = 0; + } + else if (isnext) + { + if (! apply_rcs_changes (&curlines, value, vallen, + rcs->path, + onbranch ? vers : NULL, + onbranch ? NULL : prev_vers)) + error (1, 0, "invalid change text in %s", rcs->path); + } + break; + } + } + + if (isversion) + { + /* This is either the version we want, or it is the + branchpoint to the version we want. */ + if (STREQ (branchversion, version)) + { + /* This is the version we want. */ + linevector_copy (&headlines, &curlines); + foundhead = 1; + if (onbranch) + { + /* We have found this version by tracking up a + branch. Restore back to the lines we saved + when we left the trunk, and continue tracking + down the trunk. */ + onbranch = 0; + vers = trunk_vers; + next = vers->next; + linevector_copy (&curlines, &trunklines); + } + } + else + { + Node *p; + + /* We need to look up the branch. */ + onbranch = 1; + + if (numdots (branchversion) < 2) + { + unsigned int ln; + + /* We are leaving the trunk; save the current + lines so that we can restore them when we + continue tracking down the trunk. */ + trunk_vers = vers; + linevector_copy (&trunklines, &curlines); + + /* Reset the version information we have + accumulated so far. It only applies to the + changes from the head to this version. */ + for (ln = 0; ln < curlines.nlines; ++ln) + curlines.vector[ln]->vers = NULL; + } + + /* The next version we want is the entry on + VERS->branches which matches this branch. For + example, suppose VERSION is 1.21.4.3 and + BRANCHVERSION was 1.21. Then we look for an entry + starting with "1.21.4" and we'll put it (probably + 1.21.4.1) in NEXT. We'll advance BRANCHVERSION by + two dots (in this example, to 1.21.4.3). */ + + if (vers->branches == NULL) + error (1, 0, "missing expected branches in %s", + rcs->path); + *cpversion = '.'; + ++cpversion; + cpversion = strchr (cpversion, '.'); + if (cpversion == NULL) + error (1, 0, "version number confusion in %s", + rcs->path); + for (p = vers->branches->list->next; + p != vers->branches->list; + p = p->next) + if (strncmp (p->key, branchversion, + cpversion - branchversion) == 0) + break; + if (p == vers->branches->list) + error (1, 0, "missing expected branch in %s", + rcs->path); + + next = p->key; + + cpversion = strchr (cpversion + 1, '.'); + if (cpversion != NULL) + *cpversion = '\0'; + } + } + if (op == RCS_FETCH && foundhead) + break; + } while (next != NULL); + + free (branchversion); + + rcsbuf_cache (rcs, rcsbuf); + + if (! foundhead) + error (1, 0, "could not find desired version %s in %s", + version, rcs->path); + + /* Now print out or return the data we have just computed. */ + switch (op) + { + case RCS_ANNOTATE: + { + unsigned int ln; + + for (ln = 0; ln < headlines.nlines; ++ln) + { + char buf[80]; + /* Period which separates year from month in date. */ + char *ym; + /* Period which separates month from day in date. */ + char *md; + RCSVers *prvers; + + prvers = headlines.vector[ln]->vers; + if (prvers == NULL) + prvers = vers; + + sprintf (buf, "%-12s (%-8.8s ", + prvers->version, + prvers->author); + cvs_output (buf, 0); + + /* Now output the date. */ + ym = strchr (prvers->date, '.'); + if (ym == NULL) + { + cvs_output ("??", 0); + cvs_output ("-???", 0); + cvs_output ("-??", 0); + } + else + { + md = strchr (ym + 1, '.'); + if (md == NULL) + cvs_output ("??", 0); + else + cvs_output (md + 1, 2); + + cvs_output ("-", 1); + cvs_output (month_printname (ym + 1), 0); + cvs_output ("-", 1); + /* Only output the last two digits of the year. Our output + lines are long enough as it is without printing the + century. */ + cvs_output (ym - 2, 2); + } + cvs_output ("): ", 0); + if (headlines.vector[ln]->len != 0) + cvs_output (headlines.vector[ln]->text, + headlines.vector[ln]->len); + cvs_output ("\n", 1); + } + } + break; + case RCS_FETCH: + { + char *p; + size_t n; + unsigned int ln; + + assert (text != NULL); + assert (len != NULL); + + n = 0; + for (ln = 0; ln < headlines.nlines; ++ln) + /* 1 for \n */ + n += headlines.vector[ln]->len + 1; + p = xmalloc (n); + *text = p; + for (ln = 0; ln < headlines.nlines; ++ln) + { + memcpy (p, headlines.vector[ln]->text, + headlines.vector[ln]->len); + p += headlines.vector[ln]->len; + if (headlines.vector[ln]->has_newline) + *p++ = '\n'; + } + *len = p - *text; + assert (*len <= n); + } + break; + } + + linevector_free (&curlines); + linevector_free (&headlines); + linevector_free (&trunklines); + + return; +} + +/* Read the information for a single delta from the RCS buffer RCSBUF, + whose name is RCSFILE. *KEYP and *VALP are either NULL, or the + first key/value pair to read, as set by rcsbuf_getkey. Return NULL + if there are no more deltas. Store the key/value pair which + terminated the read in *KEYP and *VALP. */ + +static RCSVers * +getdelta (struct rcsbuffer *rcsbuf, char *rcsfile, char **keyp, char **valp) +{ + RCSVers *vnode; + char *key, *value, *cp; + Node *kv; + + /* Get revision number if it wasn't passed in. This uses + rcsbuf_getkey because it doesn't croak when encountering + unexpected input. As a result, we have to play unholy games + with `key' and `value'. */ + if (*keyp != NULL) + { + key = *keyp; + value = *valp; + } + else + { + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "%s: unexpected EOF", rcsfile); + } + + /* Make sure that it is a revision number and not a cabbage + or something. */ + for (cp = key; + (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0'; + cp++) + /* do nothing */ ; + /* Note that when comparing with RCSDATE, we are not massaging + VALUE from the string found in the RCS file. This is OK since + we know exactly what to expect. */ + if (*cp != '\0' || strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) != 0) + { + *keyp = key; + *valp = value; + return NULL; + } + + vnode = (RCSVers *) xmalloc (sizeof (RCSVers)); + memset (vnode, 0, sizeof (RCSVers)); + + vnode->version = xstrdup (key); + + /* Grab the value of the date from value. Note that we are not + massaging VALUE from the string found in the RCS file. */ + cp = value + (sizeof RCSDATE) - 1; /* skip the "date" keyword */ + while (whitespace (*cp)) /* take space off front of value */ + cp++; + + vnode->date = xstrdup (cp); + + /* Get author field. */ + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + { + error (1, 0, "unexpected end of file reading %s", rcsfile); + } + if (! STREQ (key, "author")) + error (1, 0, "\ +unable to parse %s; `author' not in the expected place", rcsfile); + vnode->author = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); + + /* Get state field. */ + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + { + error (1, 0, "unexpected end of file reading %s", rcsfile); + } + if (! STREQ (key, "state")) + error (1, 0, "\ +unable to parse %s; `state' not in the expected place", rcsfile); + vnode->state = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); + /* The value is optional, according to rcsfile(5). */ + if (value != NULL && STREQ (value, RCSDEAD)) + { + vnode->dead = 1; + } + + /* Note that "branches" and "next" are in fact mandatory, according + to doc/RCSFILES. */ + + /* fill in the branch list (if any branches exist) */ + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + { + error (1, 0, "unexpected end of file reading %s", rcsfile); + } + if (STREQ (key, RCSDESC)) + { + *keyp = key; + *valp = value; + /* Probably could/should be a fatal error. */ + error (0, 0, "warning: 'branches' keyword missing from %s", rcsfile); + return vnode; + } + if (value != (char *) NULL) + { + vnode->branches = getlist (); + /* Note that we are not massaging VALUE from the string found + in the RCS file. */ + do_branches (vnode->branches, value); + } + + /* fill in the next field if there is a next revision */ + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + { + error (1, 0, "unexpected end of file reading %s", rcsfile); + } + if (STREQ (key, RCSDESC)) + { + *keyp = key; + *valp = value; + /* Probably could/should be a fatal error. */ + error (0, 0, "warning: 'next' keyword missing from %s", rcsfile); + return vnode; + } + if (value != (char *) NULL) + vnode->next = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); + + /* + * XXX - this is where we put the symbolic link stuff??? + * (into newphrases in the deltas). + */ + while (1) + { + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "unexpected end of file reading %s", rcsfile); + + /* The `desc' keyword is the end of the deltas. */ + if (strcmp (key, RCSDESC) == 0) + break; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + + /* The `hardlinks' value is a group of words, which must + be parsed separately and added as a list to vnode->hardlinks. */ + if (strcmp (key, "hardlinks") == 0) + { + char *word; + + vnode->hardlinks = getlist(); + while ((word = rcsbuf_valword (rcsbuf, &value)) != NULL) + { + Node *n = getnode(); + n->key = word; + addnode (vnode->hardlinks, n); + } + continue; + } +#endif + + /* Enable use of repositories created by certain obsolete + versions of CVS. This code should remain indefinately; + there is no procedure for converting old repositories, and + checking for it is harmless. */ + if (STREQ (key, RCSDEAD)) + { + vnode->dead = 1; + if (vnode->state != NULL) + free (vnode->state); + vnode->state = xstrdup (RCSDEAD); + continue; + } + /* if we have a new revision number, we're done with this delta */ + for (cp = key; + (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0'; + cp++) + /* do nothing */ ; + /* Note that when comparing with RCSDATE, we are not massaging + VALUE from the string found in the RCS file. This is OK + since we know exactly what to expect. */ + if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) + break; + + /* At this point, key and value represent a user-defined field + in the delta node. */ + if (vnode->other_delta == NULL) + vnode->other_delta = getlist (); + kv = getnode (); + kv->type = rcsbuf_valcmp (rcsbuf) ? RCSCMPFLD : RCSFIELD; + kv->key = xstrdup (key); + kv->data = rcsbuf_valcopy (rcsbuf, value, kv->type == RCSFIELD, + (size_t *) NULL); + if (addnode (vnode->other_delta, kv) != 0) + { + /* Complaining about duplicate keys in newphrases seems + questionable, in that we don't know what they mean and + doc/RCSFILES has no prohibition on several newphrases + with the same key. But we can't store more than one as + long as we store them in a List *. */ + error (0, 0, "warning: duplicate key `%s' in RCS file `%s'", + key, rcsfile); + freenode (kv); + } + } + + /* Return the key which caused us to fail back to the caller. */ + *keyp = key; + *valp = value; + + return vnode; +} + +static void +freedeltatext (Deltatext *d) +{ + if (d->version != NULL) + free (d->version); + if (d->log != NULL) + free (d->log); + if (d->text != NULL) + free (d->text); + if (d->other != (List *) NULL) + dellist (&d->other); + free (d); +} + +static Deltatext * +RCS_getdeltatext (RCSNode *rcs, FILE *fp, struct rcsbuffer *rcsbuf) +{ + char *num; + char *key, *value; + Node *p; + Deltatext *d; + + /* Get the revision number. */ + if (! rcsbuf_getrevnum (rcsbuf, &num)) + { + /* If num == NULL, it means we reached EOF naturally. That's + fine. */ + if (num == NULL) + return NULL; + else + error (1, 0, "%s: unexpected EOF", rcs->path); + } + + p = findnode (rcs->versions, num); + if (p == NULL) + error (1, 0, "mismatch in rcs file %s between deltas and deltatexts (%s)", + rcs->path, num); + + d = (Deltatext *) xmalloc (sizeof (Deltatext)); + d->version = xstrdup (num); + + /* Get the log message. */ + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); + if (! STREQ (key, "log")) + error (1, 0, "%s, delta %s: expected `log', got `%s'", + rcs->path, num, key); + d->log = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); + + /* Get random newphrases. */ + d->other = getlist(); + while (1) + { + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); + + if (STREQ (key, "text")) + break; + + p = getnode(); + p->type = rcsbuf_valcmp (rcsbuf) ? RCSCMPFLD : RCSFIELD; + p->key = xstrdup (key); + p->data = rcsbuf_valcopy (rcsbuf, value, p->type == RCSFIELD, + (size_t *) NULL); + if (addnode (d->other, p) < 0) + { + error (0, 0, "warning: %s, delta %s: duplicate field `%s'", + rcs->path, num, key); + } + } + + /* Get the change text. We already know that this key is `text'. */ + d->text = rcsbuf_valcopy (rcsbuf, value, 0, &d->len); + + return d; +} + +/* RCS output functions, for writing RCS format files from RCSNode + structures. + + For most of this work, RCS 5.7 uses an `aprintf' function which aborts + program upon error. Instead, these functions check the output status + of the stream right before closing it, and aborts if an error condition + is found. The RCS solution is probably the better one: it produces + more overhead, but will produce a clearer diagnostic in the case of + catastrophic error. In either case, however, the repository will probably + not get corrupted. */ + +static int +putsymbol_proc (Node *symnode, void *fparg) +{ + FILE *fp = (FILE *) fparg; + + /* A fiddly optimization: this code used to just call fprintf, but + in an old repository with hundreds of tags this can get called + hundreds of thousands of times when doing a cvs tag. Since + tagging is a relatively common operation, and using putc and + fputs is just as comprehensible, the change is worthwhile. */ + putc ('\n', fp); + putc ('\t', fp); + fputs (symnode->key, fp); + putc (':', fp); + fputs (symnode->data, fp); + return 0; +} + +static int putlock_proc (Node *, void *); + +/* putlock_proc is like putsymbol_proc, but key and data are reversed. */ + +static int +putlock_proc (Node *symnode, void *fp) +{ + return fprintf ((FILE *) fp, "\n\t%s:%s", (char *)symnode->data, symnode->key); +} + +static int +putrcsfield_proc (Node *node, void *vfp) +{ + FILE *fp = (FILE *) vfp; + + /* Some magic keys used internally by CVS start with `;'. Skip them. */ + if (node->key[0] == ';') + return 0; + + fprintf (fp, "\n%s\t", node->key); + if (node->data != NULL) + { + /* If the field's value contains evil characters, + it must be stringified. */ + /* FIXME: This does not quite get it right. "7jk8f" is not a valid + value for a value in a newpharse, according to doc/RCSFILES, + because digits are not valid in an "id". We might do OK by + always writing strings (enclosed in @@). Would be nice to + explicitly mention this one way or another in doc/RCSFILES. + A case where we are wrong in a much more clear-cut way is that + we let through non-graphic characters such as whitespace and + control characters. */ + + if (node->type == RCSCMPFLD || strpbrk (node->data, "$,.:;@") == NULL) + fputs (node->data, fp); + else + { + putc ('@', fp); + expand_at_signs (node->data, (off_t) strlen (node->data), fp); + putc ('@', fp); + } + } + + /* desc, log and text fields should not be terminated with semicolon; + all other fields should be. */ + if (! STREQ (node->key, "desc") && + ! STREQ (node->key, "log") && + ! STREQ (node->key, "text")) + { + putc (';', fp); + } + return 0; +} + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + +/* Save a filename in a `hardlinks' RCS field. NODE->KEY will contain + a full pathname, but currently only basenames are stored in the RCS + node. Assume that the filename includes nasty characters and + @-escape it. */ + +static int +puthardlink_proc (node, vfp) + Node *node; + void *vfp; +{ + FILE *fp = (FILE *) vfp; + char *basename = strrchr (node->key, '/'); + + if (basename == NULL) + basename = node->key; + else + ++basename; + + putc ('\t', fp); + putc ('@', fp); + (void) expand_at_signs (basename, strlen (basename), fp); + putc ('@', fp); + + return 0; +} + +#endif + +/* Output the admin node for RCS into stream FP. */ + +static void +RCS_putadmin (RCSNode *rcs, FILE *fp) +{ + fprintf (fp, "%s\t%s;\n", RCSHEAD, rcs->head ? rcs->head : ""); + if (rcs->branch) + fprintf (fp, "%s\t%s;\n", RCSBRANCH, rcs->branch); + + fputs ("access", fp); + if (rcs->access) + { + char *p, *s; + s = xstrdup (rcs->access); + for (p = strtok (s, " \n\t"); p != NULL; p = strtok (NULL, " \n\t")) + fprintf (fp, "\n\t%s", p); + free (s); + } + fputs (";\n", fp); + + fputs (RCSSYMBOLS, fp); + /* If we haven't had to convert the symbols to a list yet, don't + force a conversion now; just write out the string. */ + if (rcs->symbols == NULL && rcs->symbols_data != NULL) + { + fputs ("\n\t", fp); + fputs (rcs->symbols_data, fp); + } + else + walklist (RCS_symbols (rcs), putsymbol_proc, (void *) fp); + fputs (";\n", fp); + + fputs ("locks", fp); + if (rcs->locks_data) + fprintf (fp, "\t%s", rcs->locks_data); + else if (rcs->locks) + walklist (rcs->locks, putlock_proc, (void *) fp); + if (rcs->strict_locks) + fprintf (fp, "; strict"); + fputs (";\n", fp); + + if (rcs->comment) + { + fprintf (fp, "comment\t@"); + expand_at_signs (rcs->comment, (off_t) strlen (rcs->comment), fp); + fputs ("@;\n", fp); + } + if (rcs->expand && ! STREQ (rcs->expand, "kv")) + fprintf (fp, "%s\t@%s@;\n", RCSEXPAND, rcs->expand); + + walklist (rcs->other, putrcsfield_proc, (void *) fp); + + putc ('\n', fp); +} + +static void +putdelta (RCSVers *vers, FILE *fp) +{ + Node *bp, *start; + + /* Skip if no revision was supplied, or if it is outdated (cvs admin -o) */ + if (vers == NULL || vers->outdated) + return; + + fprintf (fp, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches", + vers->version, + RCSDATE, vers->date, + "author", vers->author, + "state", vers->state ? vers->state : ""); + + if (vers->branches != NULL) + { + start = vers->branches->list; + for (bp = start->next; bp != start; bp = bp->next) + fprintf (fp, "\n\t%s", bp->key); + } + + fprintf (fp, ";\nnext\t%s;", vers->next ? vers->next : ""); + + walklist (vers->other_delta, putrcsfield_proc, fp); + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (vers->hardlinks) + { + fprintf (fp, "\nhardlinks"); + walklist (vers->hardlinks, puthardlink_proc, fp); + putc (';', fp); + } +#endif + putc ('\n', fp); +} + +static void +RCS_putdtree (RCSNode *rcs, char *rev, FILE *fp) +{ + RCSVers *versp; + Node *p, *branch; + + if (rev == NULL) + return; + + /* Find the delta node for this revision. */ + p = findnode (rcs->versions, rev); + if (p == NULL) + { + error (1, 0, + "error parsing repository file %s, file may be corrupt.", + rcs->path); + } + + versp = p->data; + + /* Print the delta node and recurse on its `next' node. This prints + the trunk. If there are any branches printed on this revision, + print those trunks as well. */ + putdelta (versp, fp); + RCS_putdtree (rcs, versp->next, fp); + if (versp->branches != NULL) + { + branch = versp->branches->list; + for (p = branch->next; p != branch; p = p->next) + RCS_putdtree (rcs, p->key, fp); + } +} + +static void +RCS_putdesc (RCSNode *rcs, FILE *fp) +{ + fprintf (fp, "\n\n%s\n@", RCSDESC); + if (rcs->desc != NULL) + { + off_t len = (off_t) strlen (rcs->desc); + if (len > 0) + { + expand_at_signs (rcs->desc, len, fp); + if (rcs->desc[len-1] != '\n') + putc ('\n', fp); + } + } + fputs ("@\n", fp); +} + +static void +putdeltatext (FILE *fp, Deltatext *d) +{ + fprintf (fp, "\n\n%s\nlog\n@", d->version); + if (d->log != NULL) + { + int loglen = strlen (d->log); + expand_at_signs (d->log, (off_t) loglen, fp); + if (d->log[loglen-1] != '\n') + putc ('\n', fp); + } + putc ('@', fp); + + walklist (d->other, putrcsfield_proc, fp); + + fputs ("\ntext\n@", fp); + if (d->text != NULL) + expand_at_signs (d->text, (off_t) d->len, fp); + fputs ("@\n", fp); +} + +/* TODO: the whole mechanism for updating deltas is kludgey... more + sensible would be to supply all the necessary info in a `newdeltatext' + field for RCSVers nodes. -twp */ + +/* Copy delta text nodes from FIN to FOUT. If NEWDTEXT is non-NULL, it + is a new delta text node, and should be added to the tree at the + node whose revision number is INSERTPT. (Note that trunk nodes are + written in decreasing order, and branch nodes are written in + increasing order.) */ + +static void +RCS_copydeltas (RCSNode *rcs, FILE *fin, struct rcsbuffer *rcsbufin, FILE *fout, Deltatext *newdtext, char *insertpt) +{ + int actions; + RCSVers *dadmin; + Node *np; + int insertbefore, found; + char *bufrest; + int nls; + size_t buflen; +#ifndef HAVE_MMAP + char buf[8192]; + int got; +#endif + + /* Count the number of versions for which we have to do some + special operation. */ + actions = walklist (rcs->versions, count_delta_actions, (void *) NULL); + + /* Make a note of whether NEWDTEXT should be inserted + before or after its INSERTPT. */ + insertbefore = (newdtext != NULL && numdots (newdtext->version) == 1); + + while (actions != 0 || newdtext != NULL) + { + Deltatext *dtext; + + dtext = RCS_getdeltatext (rcs, fin, rcsbufin); + + /* We shouldn't hit EOF here, because that would imply that + some action was not taken, or that we could not insert + NEWDTEXT. */ + if (dtext == NULL) + error (1, 0, "internal error: EOF too early in RCS_copydeltas"); + + found = (insertpt != NULL && STREQ (dtext->version, insertpt)); + if (found && insertbefore) + { + putdeltatext (fout, newdtext); + newdtext = NULL; + insertpt = NULL; + } + + np = findnode (rcs->versions, dtext->version); + dadmin = np->data; + + /* If this revision has been outdated, just skip it. */ + if (dadmin->outdated) + { + freedeltatext (dtext); + --actions; + continue; + } + + /* Update the change text for this delta. New change text + data may come from cvs admin -m, cvs admin -o, or cvs ci. */ + if (dadmin->text != NULL) + { + if (dadmin->text->log != NULL || dadmin->text->text != NULL) + --actions; + if (dadmin->text->log != NULL) + { + free (dtext->log); + dtext->log = dadmin->text->log; + dadmin->text->log = NULL; + } + if (dadmin->text->text != NULL) + { + free (dtext->text); + dtext->text = dadmin->text->text; + dtext->len = dadmin->text->len; + dadmin->text->text = NULL; + } + } + putdeltatext (fout, dtext); + freedeltatext (dtext); + + if (found && !insertbefore) + { + putdeltatext (fout, newdtext); + newdtext = NULL; + insertpt = NULL; + } + } + + /* Copy the rest of the file directly, without bothering to + interpret it. The caller will handle error checking by calling + ferror. + + We just wrote a newline to the file, either in putdeltatext or + in the caller. However, we may not have read the corresponding + newline from the file, because rcsbuf_getkey returns as soon as + it finds the end of the '@' string for the desc or text key. + Therefore, we may read three newlines when we should really + only write two, and we check for that case here. This is not + an semantically important issue; we only do it to make our RCS + files look traditional. */ + + nls = 3; + + rcsbuf_get_buffered (rcsbufin, &bufrest, &buflen); + if (buflen > 0) + { + if (bufrest[0] != '\n' + || strncmp (bufrest, "\n\n\n", buflen < 3 ? buflen : 3) != 0) + { + nls = 0; + } + else + { + if (buflen < 3) + nls -= buflen; + else + { + ++bufrest; + --buflen; + nls = 0; + } + } + + fwrite (bufrest, 1, buflen, fout); + } +#ifndef HAVE_MMAP + /* This bit isn't necessary when using mmap since the entire file + * will already be available via the RCS buffer. Besides, the + * mmap code doesn't always keep the file pointer up to date, so + * this adds some data twice. + */ + while ((got = fread (buf, 1, sizeof buf, fin)) != 0) + { + if (nls > 0 + && got >= nls + && buf[0] == '\n' + && strncmp (buf, "\n\n\n", nls) == 0) + { + fwrite (buf + 1, 1, got - 1, fout); + } + else + { + fwrite (buf, 1, got, fout); + } + + nls = 0; + } +#endif /* HAVE_MMAP */ +} + +/* A helper procedure for RCS_copydeltas. This is called via walklist + to count the number of RCS revisions for which some special action + is required. */ + +static int +count_delta_actions (Node *np, void *ignore) +{ + RCSVers *dadmin = np->data; + + if (dadmin->outdated) + return 1; + + if (dadmin->text != NULL + && (dadmin->text->log != NULL || dadmin->text->text != NULL)) + { + return 1; + } + + return 0; +} + + + +/* + * Clean up temporary files. + * + * NOTES + * This function needs to be reentrant since a call to exit() can cause a + * call to this function, which can then be interrupted by a signal, which + * can cause a second call to this function. + * + * RETURNS + * Nothing. + */ +static void +rcs_cleanup (void) +{ + static short int never_run_again = 0; + + TRACE (TRACE_FUNCTION, "rcs_cleanup()"); + + /* Since main_cleanup() always calls exit() (via error (1, ...)), we avoid + * allowing this function to be called twice as an optimization. + * + * If we are already in a signal critical section, assume we were called + * via the signal handler and set a flag which will prevent future calls. + * The only time that we should get into one of these functions otherwise + * while still in a critical section is if error(1,...) is called from a + * critical section, in which case we are exiting and interrupts are + * already being ignored. + * + * For Lock_Cleanup(), this is not a run_once variable since Lock_Cleanup() + * can be called to clean up the current lock set multiple times by the + * same run of a CVS command. + * + * For server_cleanup() and rcs_cleanup(), this is not a run_once variable + * since a call to the cleanup function from atexit() could be interrupted + * by the interrupt handler. + */ + if (never_run_again) return; + if (SIG_inCrSect()) never_run_again = 1; + + /* FIXME: Do not perform buffered I/O from an interrupt handler like + * this (via error). However, I'm leaving the error-calling code there + * in the hope that on the rare occasion the error call is actually made + * (e.g., a fluky I/O error or permissions problem prevents the deletion + * of a just-created file) reentrancy won't be an issue. + */ + + /* Avoid being interrupted during calls which set globals to NULL. This + * avoids having interrupt handlers attempt to use these global variables + * in inconsistent states. + */ + SIG_beginCrSect(); + if (rcs_lockfile != NULL) + { + /* Use a tmp var since any of these functions could call exit, causing + * us to be called a second time. + */ + char *tmp = rcs_lockfile; + rcs_lockfile = NULL; + if (rcs_lockfd >= 0) + { + if (close (rcs_lockfd) != 0) + error (0, errno, "error closing lock file %s", tmp); + rcs_lockfd = -1; + } + + /* Note that the checks for existence_error are because we can be + * called from a signal handler, so we don't know whether the + * files got created. + */ + if (unlink_file (tmp) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", tmp); + } + SIG_endCrSect(); +} + + + +/* RCS_internal_lockfile and RCS_internal_unlockfile perform RCS-style + locking on the specified RCSFILE: for a file called `foo,v', open + for writing a file called `,foo,'. + + Note that we what do here is quite different from what RCS does. + RCS creates the ,foo, file before it reads the RCS file (if it + knows that it will be writing later), so that it actually serves as + a lock. We don't; instead we rely on CVS writelocks. This means + that if someone is running RCS on the file at the same time they + are running CVS on it, they might lose (we read the file, + then RCS writes it, then we write it, clobbering the + changes made by RCS). I believe the current sentiment about this + is "well, don't do that". + + A concern has been expressed about whether adopting the RCS + strategy would slow us down. I don't think so, since we need to + write the ,foo, file anyway (unless perhaps if O_EXCL is slower or + something). + + These do not perform quite the same function as the RCS -l option + for locking files: they are intended to prevent competing RCS + processes from stomping all over each other's laundry. Hence, + they are `internal' locking functions. + + If there is an error, give a fatal error; if we return we always + return a non-NULL value. */ + +static FILE * +rcs_internal_lockfile (char *rcsfile) +{ + struct stat rstat; + FILE *fp; + static int first_call = 1; + + if (first_call) + { + first_call = 0; + /* Clean up if we get a signal or exit. */ + cleanup_register (rcs_cleanup); + } + + /* Get the lock file name: `,file,' for RCS file `file,v'. */ + assert (rcs_lockfile == NULL); + assert (rcs_lockfd < 0); + rcs_lockfile = rcs_lockfilename (rcsfile); + + /* Use the existing RCS file mode, or read-only if this is a new + file. (Really, this is a lie -- if this is a new file, + RCS_checkin uses the permissions from the working copy. For + actually creating the file, we use 0444 as a safe default mode.) */ + if( CVS_STAT( rcsfile, &rstat ) < 0 ) + { + if (existence_error (errno)) + rstat.st_mode = S_IRUSR | S_IRGRP | S_IROTH; + else + error (1, errno, "cannot stat %s", rcsfile); + } + + /* Try to open exclusively. POSIX.1 guarantees that O_EXCL|O_CREAT + guarantees an exclusive open. According to the RCS source, with + NFS v2 we must also throw in O_TRUNC and use an open mask that makes + the file unwriteable. For extensive justification, see the comments for + rcswriteopen() in rcsedit.c, in RCS 5.7. This is kind of pointless + in the CVS case; see comment at the start of this file concerning + general ,foo, file strategy. + + There is some sentiment that with NFSv3 and such, that one can + rely on O_EXCL these days. This might be true for unix (I + don't really know), but I am still pretty skeptical in the case + of the non-unix systems. */ + rcs_lockfd = open (rcs_lockfile, + OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, + S_IRUSR | S_IRGRP | S_IROTH); + + if (rcs_lockfd < 0) + { + error (1, errno, "could not open lock file `%s'", rcs_lockfile); + } + + /* Force the file permissions, and return a stream object. */ + /* Because we change the modes later, we don't worry about + this in the non-HAVE_FCHMOD case. */ +#ifdef HAVE_FCHMOD + if (fchmod (rcs_lockfd, rstat.st_mode) < 0) + error (1, errno, "cannot change mode for %s", rcs_lockfile); +#endif + fp = fdopen (rcs_lockfd, FOPEN_BINARY_WRITE); + if (fp == NULL) + error (1, errno, "cannot fdopen %s", rcs_lockfile); + + return fp; +} + +static void +rcs_internal_unlockfile (FILE *fp, char *rcsfile) +{ + assert (rcs_lockfile != NULL); + assert (rcs_lockfd >= 0); + + /* Abort if we could not write everything successfully to LOCKFILE. + This is not a great error-handling mechanism, but should prevent + corrupting the repository. */ + + if (ferror (fp)) + /* Using errno here may well be misleanding since the most recent + call that set errno may not have anything whatsoever to do with + the error that set the flag, but it's better than nothing. The + real solution is to check each call to fprintf rather than waiting + until the end like this. */ + error (1, errno, "error writing to lock file %s", rcs_lockfile); + if (fclose (fp) == EOF) + error (1, errno, "error closing lock file %s", rcs_lockfile); + rcs_lockfd = -1; + + rename_file (rcs_lockfile, rcsfile); + + { + /* Use a temporary to make sure there's no interval + (after rcs_lockfile has been freed but before it's set to NULL) + during which the signal handler's use of rcs_lockfile would + reference freed memory. */ + char *tmp = rcs_lockfile; + rcs_lockfile = NULL; + free (tmp); + } +} + +static char * +rcs_lockfilename (const char *rcsfile) +{ + char *lockfile, *lockp; + const char *rcsbase, *rcsp, *rcsend; + int rcslen; + + /* Create the lockfile name. */ + rcslen = strlen (rcsfile); + lockfile = (char *) xmalloc (rcslen + 10); + rcsbase = last_component (rcsfile); + rcsend = rcsfile + rcslen - sizeof(RCSEXT); + for (lockp = lockfile, rcsp = rcsfile; rcsp < rcsbase; ++rcsp) + *lockp++ = *rcsp; + *lockp++ = ','; + while (rcsp <= rcsend) + *lockp++ = *rcsp++; + *lockp++ = ','; + *lockp = '\0'; + + return lockfile; +} + +/* Rewrite an RCS file. The basic idea here is that the caller should + first call RCS_reparsercsfile, then munge the data structures as + desired (via RCS_delete_revs, RCS_settag, &c), then call RCS_rewrite. */ + +void +RCS_rewrite (RCSNode *rcs, Deltatext *newdtext, char *insertpt) +{ + FILE *fin, *fout; + struct rcsbuffer rcsbufin; + + if (noexec) + return; + + /* Make sure we're operating on an actual file and not a symlink. */ + resolve_symlink (&(rcs->path)); + + fout = rcs_internal_lockfile (rcs->path); + + RCS_putadmin (rcs, fout); + RCS_putdtree (rcs, rcs->head, fout); + RCS_putdesc (rcs, fout); + + /* Open the original RCS file and seek to the first delta text. */ + rcsbuf_cache_open (rcs, rcs->delta_pos, &fin, &rcsbufin); + + /* Update delta_pos to the current position in the output file. + Do NOT move these statements: they must be done after fin has + been positioned at the old delta_pos, but before any delta + texts have been written to fout. + */ + rcs->delta_pos = ftello (fout); + if (rcs->delta_pos == -1) + error (1, errno, "cannot ftello in RCS file %s", rcs->path); + + RCS_copydeltas (rcs, fin, &rcsbufin, fout, newdtext, insertpt); + + /* We don't want to call rcsbuf_cache here, since we're about to + delete the file. */ + rcsbuf_close (&rcsbufin); + if (ferror (fin)) + /* The only case in which using errno here would be meaningful + is if we happen to have left errno unmolested since the call + which produced the error (e.g. fread). That is pretty + fragile even if it happens to sometimes be true. The real + solution is to make sure that all the code which reads + from fin checks for errors itself (some does, some doesn't). */ + error (0, 0, "warning: ferror set while rewriting RCS file `%s'", rcs->path); + if (fclose (fin) < 0) + error (0, errno, "warning: closing RCS file `%s'", rcs->path); + + rcs_internal_unlockfile (fout, rcs->path); +} + +/* Abandon changes to an RCS file. */ + +void +RCS_abandon (RCSNode *rcs) +{ + free_rcsnode_contents (rcs); + rcs->symbols_data = NULL; + rcs->expand = NULL; + rcs->access = NULL; + rcs->locks_data = NULL; + rcs->comment = NULL; + rcs->desc = NULL; + rcs->flags |= PARTIAL; +} + +/* + * For a given file with full pathname PATH and revision number REV, + * produce a file label suitable for passing to diff. The default + * file label as used by RCS 5.7 looks like this: + * + * FILENAME YYYY/MM/DD HH:MM:SS REVNUM + * + * The date and time used are the revision's last checkin date and time. + * If REV is NULL, use the working copy's mtime instead. + * + * /dev/null is not statted but assumed to have been created on the Epoch. + * At least using the POSIX.2 definition of patch, this should cause creation + * of files on platforms such as Windoze where the null IO device isn't named + * /dev/null to be parsed by patch properly. + */ +char * +make_file_label (const char *path, const char *rev, RCSNode *rcs) +{ + char datebuf[MAXDATELEN + 1]; + char *label; + + label = (char *) xmalloc (strlen (path) + + (rev == NULL ? 0 : strlen (rev) + 1) + + MAXDATELEN + + 2); + + if (rev) + { + char date[MAXDATELEN + 1]; + /* revs cannot be attached to /dev/null ... duh. */ + assert (strcmp(DEVNULL, path)); + RCS_getrevtime (rcs, rev, datebuf, 0); + (void) date_to_internet (date, datebuf); + (void) sprintf (label, "-L%s\t%s\t%s", path, date, rev); + } + else + { + struct stat sb; + struct tm *wm; + + if (strcmp(DEVNULL, path)) + { + const char *file = last_component (path); + if (CVS_STAT (file, &sb) < 0) + /* Assume that if the stat fails,then the later read for the + * diff will too. + */ + error (1, errno, "could not get info for `%s'", path); + wm = gmtime (&sb.st_mtime); + } + else + { + time_t t = 0; + wm = gmtime(&t); + } + + (void) tm_to_internet (datebuf, wm); + (void) sprintf (label, "-L%s\t%s", path, datebuf); + } + return label; +} + +void +RCS_setlocalid (const char *arg) +{ + char *copy, *next, *key; + + copy = xstrdup(arg); + next = copy; + key = strtok(next, "="); + + keywords[KEYWORD_LOCALID].string = xstrdup(key); + keywords[KEYWORD_LOCALID].len = strlen(key); + keywords[KEYWORD_LOCALID].expandit = 1; + + /* options? */ + while ((key = strtok(NULL, ",")) != NULL) { + if (!strcmp(key, keywords[KEYWORD_ID].string)) + keyword_local = KEYWORD_ID; + else if (!strcmp(key, keywords[KEYWORD_HEADER].string)) + keyword_local = KEYWORD_HEADER; + else if (!strcmp(key, keywords[KEYWORD_CVSHEADER].string)) + keyword_local = KEYWORD_CVSHEADER; + else + error(1, 0, "Unknown LocalId mode: %s", key); + } + free(copy); +} + +void +RCS_setincexc (const char *arg) +{ + char *key; + char *copy, *next; + int include = 0; + struct rcs_keyword *keyword; + + copy = xstrdup(arg); + next = copy; + switch (*next++) { + case 'e': + include = 0; + break; + case 'i': + include = 1; + break; + default: + free(copy); + return; + } + + if (include) + for (keyword = keywords; keyword->string != NULL; keyword++) + { + keyword->expandit = 0; + } + + key = strtok(next, ","); + while (key) { + for (keyword = keywords; keyword->string != NULL; keyword++) { + if (strcmp (keyword->string, key) == 0) + keyword->expandit = include; + } + key = strtok(NULL, ","); + } + free(copy); + return; +} + +#define ATTIC "/" CVSATTIC +static char * +getfullCVSname(char *CVSname, char **pathstore) +{ + if (current_parsed_root->directory) { + int rootlen; + char *c = NULL; + int alen = sizeof(ATTIC) - 1; + + *pathstore = xstrdup(CVSname); + if ((c = strrchr(*pathstore, '/')) != NULL) { + if (c - *pathstore >= alen) { + if (!strncmp(c - alen, ATTIC, alen)) { + while (*c != '\0') { + *(c - alen) = *c; + c++; + } + *(c - alen) = '\0'; + } + } + } + + rootlen = strlen(current_parsed_root->directory); + if (!strncmp(*pathstore, current_parsed_root->directory, rootlen) && + (*pathstore)[rootlen] == '/') + CVSname = (*pathstore + rootlen + 1); + else + CVSname = (*pathstore); + } + return CVSname; +} diff --git a/contrib/cvs-1.12.9/src/rcs.h b/contrib/cvs-1.12.9/src/rcs.h new file mode 100644 index 0000000000..c8705ab956 --- /dev/null +++ b/contrib/cvs-1.12.9/src/rcs.h @@ -0,0 +1,251 @@ +/* + * 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 source distribution. + * + * RCS source control definitions needed by rcs.c and friends + */ + +/* Strings which indicate a conflict if they occur at the start of a line. */ +#define RCS_MERGE_PAT_1 "<<<<<<< " +#define RCS_MERGE_PAT_2 "=======\n" +#define RCS_MERGE_PAT_3 ">>>>>>> " + +#define RCSEXT ",v" +#define RCSPAT "*,v" +#define RCSHEAD "head" +#define RCSBRANCH "branch" +#define RCSSYMBOLS "symbols" +#define RCSDATE "date" +#define RCSDESC "desc" +#define RCSEXPAND "expand" + +/* Used by the version of death support which resulted from old + versions of CVS (e.g. 1.5 if you define DEATH_SUPPORT and not + DEATH_STATE). Only a hacked up RCS (used by those old versions of + CVS) will put this into RCS files. Considered obsolete. */ +#define RCSDEAD "dead" + +#define DATEFORM "%02d.%02d.%02d.%02d.%02d.%02d" +#define SDATEFORM "%d.%d.%d.%d.%d.%d" + +/* + * Opaque structure definitions used by RCS specific lookup routines + */ +#define VALID 0x1 /* flags field contains valid data */ +#define INATTIC 0x2 /* RCS file is located in the Attic */ +#define PARTIAL 0x4 /* RCS file not completly parsed */ + +/* All the "char *" fields in RCSNode, Deltatext, and RCSVers are + '\0'-terminated (except "text" in Deltatext). This means that we + can't deal with fields containing '\0', which is a limitation that + RCS does not have. Would be nice to fix this some day. */ + +struct rcsnode +{ + /* Reference count for this structure. Used to deal with the + fact that there might be a pointer from the Vers_TS or might + not. Callers who increment this field are responsible for + calling freercsnode when they are done with their reference. */ + int refcount; + + /* Flags (INATTIC, PARTIAL, &c), see above. */ + 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; + + /* Value for head keyword from RCS header, or NULL if empty. */ + char *head; + + /* Value for branch keyword from RCS header, or NULL if omitted. */ + char *branch; + + /* Raw data on symbolic revisions. The first time that RCS_symbols is + called, we parse these into ->symbols, and free ->symbols_data. */ + char *symbols_data; + + /* Value for expand keyword from RCS header, or NULL if omitted. */ + char *expand; + + /* List of nodes, the key of which is the symbolic name and the data + of which is the numeric revision that it corresponds to (malloc'd). */ + List *symbols; + + /* List of nodes (type RCSVERS), the key of which the numeric revision + number, and the data of which is an RCSVers * for the revision. */ + List *versions; + + /* Value for access keyword from RCS header, or NULL if empty. + FIXME: RCS_delaccess would also seem to use "" for empty. We + should pick one or the other. */ + char *access; + + /* Raw data on locked revisions. The first time that RCS_getlocks is + called, we parse these into ->locks, and free ->locks_data. */ + char *locks_data; + + /* List of nodes, the key of which is the numeric revision and the + data of which is the user that it corresponds to (malloc'd). */ + List *locks; + + /* Set for the strict keyword from the RCS header. */ + int strict_locks; + + /* Value for the comment keyword from RCS header (comment leader), or + NULL if omitted. */ + char *comment; + + /* Value for the desc field in the RCS file, or NULL if empty. */ + char *desc; + + /* File offset of the first deltatext node, so we can seek there. */ + off_t delta_pos; + + /* Newphrases from the RCS header. List of nodes, the key of which + is the "id" which introduces the newphrase, and the value of which + is the value from the newphrase. */ + List *other; +}; + +typedef struct rcsnode RCSNode; + +struct deltatext { + char *version; + + /* Log message, or NULL if we do not intend to change the log message + (that is, RCS_copydeltas should just use the log message from the + file). */ + char *log; + + /* Change text, or NULL if we do not intend to change the change text + (that is, RCS_copydeltas should just use the change text from the + file). Note that it is perfectly valid to have log be NULL and + text non-NULL, or vice-versa. */ + char *text; + size_t len; + + /* Newphrase fields from deltatext nodes. FIXME: duplicates the + other field in the rcsversnode, I think. */ + List *other; +}; +typedef struct deltatext Deltatext; + +struct rcsversnode +{ + /* Duplicate of the key by which this structure is indexed. */ + char *version; + + char *date; + char *author; + char *state; + char *next; + int dead; + int outdated; + Deltatext *text; + List *branches; + /* Newphrase fields from deltatext nodes. Also contains ";add" and + ";delete" magic fields (see rcs.c, log.c). I think this is + only used by log.c (where it looks up "log"). Duplicates the + other field in struct deltatext, I think. */ + List *other; + /* Newphrase fields from delta nodes. */ + List *other_delta; +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Hard link information for each revision. */ + List *hardlinks; +#endif +}; +typedef struct rcsversnode RCSVers; + +/* + * CVS reserves all even-numbered branches for its own use. "magic" branches + * (see rcs.c) are contained as virtual revision numbers (within symbolic + * tags only) off the RCS_MAGIC_BRANCH, which is 0. CVS also reserves the + * ".1" branch for vendor revisions. So, if you do your own branching, you + * should limit your use to odd branch numbers starting at 3. + */ +#define RCS_MAGIC_BRANCH 0 + +/* The type of a function passed to RCS_checkout. */ +typedef void (*RCSCHECKOUTPROC) (void *, const char *, size_t); + +struct rcsbuffer; + +/* What RCS_deltas is supposed to do. */ +enum rcs_delta_op {RCS_ANNOTATE, RCS_FETCH}; + +/* + * exported interfaces + */ +RCSNode *RCS_parse (const char *file, const char *repos); +RCSNode *RCS_parsercsfile (const char *rcsfile); +void RCS_fully_parse (RCSNode *); +void RCS_reparsercsfile (RCSNode *, FILE **, struct rcsbuffer *); +extern int RCS_setattic (RCSNode *, int); + +char *RCS_check_kflag (const char *arg); +char *RCS_getdate (RCSNode * rcs, const char *date, int force_tag_match); +char *RCS_gettag (RCSNode * rcs, const char *symtag, int force_tag_match, + int *simple_tag); +int RCS_exist_rev (RCSNode *rcs, char *rev); +int RCS_exist_tag (RCSNode *rcs, char *tag); +char *RCS_tag2rev (RCSNode *rcs, char *tag); +char *RCS_getversion (RCSNode *rcs, const char *tag, const char *date, + int force_tag_match, int *simple_tag); +char *RCS_magicrev (RCSNode *rcs, char *rev); +int RCS_isbranch (RCSNode *rcs, const char *rev); +int RCS_nodeisbranch (RCSNode *rcs, const char *tag); +char *RCS_whatbranch (RCSNode *rcs, const char *tag); +char *RCS_head (RCSNode * rcs); +int RCS_datecmp (const char *date1, const char *date2); +time_t RCS_getrevtime (RCSNode * rcs, const char *rev, char *date, int fudge); +List *RCS_symbols (RCSNode *rcs); +void RCS_check_tag (const char *tag); +int RCS_valid_rev (char *rev); +List *RCS_getlocks (RCSNode *rcs); +void freercsnode (RCSNode ** rnodep); +char *RCS_getbranch (RCSNode *rcs, const char *tag, int force_tag_match); +char *RCS_branch_head (RCSNode *rcs, char *rev); + +int RCS_isdead (RCSNode *, const char *); +char *RCS_getexpand (RCSNode *); +void RCS_setexpand (RCSNode *, const char *); +int RCS_checkout (RCSNode *, const char *, const char *, const char *, + const char *, const char *, RCSCHECKOUTPROC, void *); +int RCS_checkin (RCSNode *rcs, const char *workfile, const char *message, + const char *rev, int flags); +int RCS_cmp_file (RCSNode *, const char *, char **, const char *, const char *, + const char * ); +int RCS_settag (RCSNode *, const char *, const char *); +int RCS_deltag (RCSNode *, const char *); +int RCS_setbranch (RCSNode *, const char *); +int RCS_lock (RCSNode *, const char *, int); +int RCS_unlock (RCSNode *, char *, int); +int RCS_delete_revs (RCSNode *, char *, char *, int); +void RCS_addaccess (RCSNode *, char *); +void RCS_delaccess (RCSNode *, char *); +char *RCS_getaccess (RCSNode *); +void RCS_rewrite (RCSNode *, Deltatext *, char *); +void RCS_abandon (RCSNode *); +int rcs_change_text (const char *, char *, size_t, const char *, + size_t, char **, size_t *); +void RCS_deltas (RCSNode *, FILE *, struct rcsbuffer *, const char *, + enum rcs_delta_op, char **, size_t *, + char **, size_t *); +void RCS_setincexc (const char *arg); +void RCS_setlocalid (const char *arg); +char *make_file_label (const char *, const char *, RCSNode *); + +extern int preserve_perms; + +/* From import.c. */ +extern int add_rcs_file (const char *, const char *, const char *, + const char *, const char *, const char *, + const char *, int, char **, const char *, size_t, + FILE *); diff --git a/contrib/cvs-1.12.9/src/rcscmds.c b/contrib/cvs-1.12.9/src/rcscmds.c new file mode 100644 index 0000000000..af9ff3b41c --- /dev/null +++ b/contrib/cvs-1.12.9/src/rcscmds.c @@ -0,0 +1,609 @@ +/* + * 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 source distribution. + * + * The functions in this file provide an interface for performing + * operations directly on RCS files. + */ + +#include "cvs.h" +#include +#include "diffrun.h" + +/* This file, rcs.h, and rcs.c, together sometimes known as the "RCS + library", are intended to define our interface to RCS files. + + 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 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 a related note, see the comments at diff_exec, later in this file, + for more on the diff library. */ + +static void RCS_output_diff_options (const char *, const char *, const char *, + const char *); + + +/* Stuff to deal with passing arguments the way libdiff.a wants to deal + with them. This is a crufty interface; there is no good reason for it + to resemble a command line rather than something closer to "struct + log_data" in log.c. */ + +/* First call call_diff_setup to setup any initial arguments. The + argument will be parsed into whitespace separated words and added + to the global call_diff_argv list. + + Then, optionally, call call_diff_arg for each additional argument + that you'd like to pass to the diff library. + + Finally, call call_diff or call_diff3 to produce the diffs. */ + +static char **call_diff_argv; +static int call_diff_argc; +static int call_diff_argc_allocated; + +static void call_diff_add_arg (const char *); +static void call_diff_setup (const char *prog); +static int call_diff (const char *out); +static int call_diff3 (char *out); + +static void call_diff_write_output (const char *, size_t); +static void call_diff_flush_output (void); +static void call_diff_write_stdout (const char *); +static void call_diff_error (const char *, const char *, const char *); + +/* VARARGS */ +static void +call_diff_setup (const char *prog) +{ + char *cp; + int i; + char *call_diff_prog; + + /* clean out any malloc'ed values from call_diff_argv */ + for (i = 0; i < call_diff_argc; i++) + { + if (call_diff_argv[i]) + { + free (call_diff_argv[i]); + call_diff_argv[i] = (char *) 0; + } + } + call_diff_argc = 0; + + call_diff_prog = xstrdup (prog); + + /* put each word into call_diff_argv, allocating it as we go */ + for (cp = strtok (call_diff_prog, " \t"); + cp != NULL; + cp = strtok ((char *) NULL, " \t")) + call_diff_add_arg (cp); + free (call_diff_prog); +} + +static void +call_diff_arg (const char *s) +{ + call_diff_add_arg (s); +} + +static void +call_diff_add_arg (const char *s) +{ + /* allocate more argv entries if we've run out */ + if (call_diff_argc >= call_diff_argc_allocated) + { + call_diff_argc_allocated += 50; + call_diff_argv = (char **) + xrealloc ((char *) call_diff_argv, + call_diff_argc_allocated * sizeof (char **)); + } + + if (s) + call_diff_argv[call_diff_argc++] = xstrdup (s); + else + /* Not post-incremented on purpose! */ + call_diff_argv[call_diff_argc] = (char *) 0; +} + +/* Callback function for the diff library to write data to the output + file. This is used when we are producing output to stdout. */ + +static void +call_diff_write_output (const char *text, size_t len) +{ + if (len > 0) + cvs_output (text, len); +} + +/* Call back function for the diff library to flush the output file. + This is used when we are producing output to stdout. */ + +static void +call_diff_flush_output (void) +{ + cvs_flushout (); +} + +/* Call back function for the diff library to write to stdout. */ + +static void +call_diff_write_stdout (const char *text) +{ + cvs_output (text, 0); +} + +/* Call back function for the diff library to write to stderr. */ + +static void +call_diff_error (const char *format, const char *a1, const char *a2) +{ + /* FIXME: Should we somehow indicate that this error is coming from + the diff library? */ + error (0, 0, format, a1, a2); +} + +/* This set of callback functions is used if we are sending the diff + to stdout. */ + +static struct diff_callbacks call_diff_stdout_callbacks = +{ + call_diff_write_output, + call_diff_flush_output, + call_diff_write_stdout, + call_diff_error +}; + +/* This set of callback functions is used if we are sending the diff + to a file. */ + +static struct diff_callbacks call_diff_file_callbacks = +{ + (void (*) (const char *, size_t)) NULL, + (void (*) (void)) NULL, + call_diff_write_stdout, + call_diff_error +}; + + + +static int +call_diff (const char *out) +{ + if (out == RUN_TTY) + return diff_run( call_diff_argc, call_diff_argv, NULL, + &call_diff_stdout_callbacks ); + else + return diff_run( call_diff_argc, call_diff_argv, out, + &call_diff_file_callbacks ); +} + + + +static int +call_diff3 (char *out) +{ + if (out == RUN_TTY) + return diff3_run (call_diff_argc, call_diff_argv, NULL, + &call_diff_stdout_callbacks); + else + return diff3_run (call_diff_argc, call_diff_argv, out, + &call_diff_file_callbacks); +} + + + +/* Merge revisions REV1 and REV2. */ + +int +RCS_merge (RCSNode *rcs, const char *path, const char *workfile, + const char *options, const char *rev1, const char *rev2) +{ + char *xrev1, *xrev2; + char *tmp1, *tmp2; + char *diffout = NULL; + int retval; + + if (options != NULL && options[0] != '\0') + assert (options[0] == '-' && options[1] == 'k'); + + cvs_output ("RCS file: ", 0); + cvs_output (rcs->path, 0); + cvs_output ("\n", 1); + + /* Calculate numeric revision numbers from rev1 and rev2 (may be + symbolic). + FIXME - No they can't. Both calls to RCS_merge are passing in + numeric revisions. */ + xrev1 = RCS_gettag (rcs, rev1, 0, NULL); + xrev2 = RCS_gettag (rcs, rev2, 0, NULL); + + /* Check out chosen revisions. The error message when RCS_checkout + fails is not very informative -- it is taken verbatim from RCS 5.7, + and relies on RCS_checkout saying something intelligent upon failure. */ + cvs_output ("retrieving revision ", 0); + cvs_output (xrev1, 0); + cvs_output ("\n", 1); + + tmp1 = cvs_temp_name(); + if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1, + (RCSCHECKOUTPROC)0, NULL)) + { + cvs_outerr ("rcsmerge: co failed\n", 0); + exit (EXIT_FAILURE); + } + + cvs_output ("retrieving revision ", 0); + cvs_output (xrev2, 0); + cvs_output ("\n", 1); + + tmp2 = cvs_temp_name(); + if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2, + (RCSCHECKOUTPROC)0, NULL)) + { + cvs_outerr ("rcsmerge: co failed\n", 0); + exit (EXIT_FAILURE); + } + + /* Merge changes. */ + cvs_output ("Merging differences between ", 0); + cvs_output (xrev1, 0); + cvs_output (" and ", 0); + cvs_output (xrev2, 0); + cvs_output (" into ", 0); + cvs_output (workfile, 0); + cvs_output ("\n", 1); + + /* Remember that the first word in the `call_diff_setup' string is used now + only for diagnostic messages -- CVS no longer forks to run diff3. */ + diffout = cvs_temp_name(); + call_diff_setup ("diff3"); + call_diff_arg ("-E"); + call_diff_arg ("-am"); + + call_diff_arg ("-L"); + call_diff_arg (workfile); + call_diff_arg ("-L"); + call_diff_arg (xrev1); + call_diff_arg ("-L"); + call_diff_arg (xrev2); + + call_diff_arg ("--"); + call_diff_arg (workfile); + call_diff_arg (tmp1); + call_diff_arg (tmp2); + + retval = call_diff3 (diffout); + + if (retval == 1) + cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0); + else if (retval == 2) + exit (EXIT_FAILURE); + + if (diffout) + copy_file (diffout, workfile); + + /* Clean up. */ + { + int save_noexec = noexec; + noexec = 0; + if (unlink_file (tmp1) < 0) + { + if (!existence_error (errno)) + error (0, errno, "cannot remove temp file %s", tmp1); + } + free (tmp1); + if (unlink_file (tmp2) < 0) + { + if (!existence_error (errno)) + error (0, errno, "cannot remove temp file %s", tmp2); + } + free (tmp2); + if (diffout) + { + if (unlink_file (diffout) < 0) + { + if (!existence_error (errno)) + error (0, errno, "cannot remove temp file %s", diffout); + } + free (diffout); + } + free (xrev1); + free (xrev2); + noexec = save_noexec; + } + + return retval; +} + +/* Diff revisions and/or files. OPTS controls the format of the diff + (it contains options such as "-w -c", &c), or "" for the default. + OPTIONS controls keyword expansion, as a string starting with "-k", + or "" to use the default. REV1 is the first revision to compare + against; it must be non-NULL. If REV2 is non-NULL, compare REV1 + and REV2; if REV2 is NULL compare REV1 with the file in the working + directory, whose name is WORKFILE. LABEL1 and LABEL2 are default + file labels, and (if non-NULL) should be added as -L options + to diff. Output goes to stdout. + + Return value is 0 for success, -1 for a failure which set errno, + or positive for a failure which printed a message on stderr. + + This used to exec rcsdiff, but now calls RCS_checkout and diff_exec. + + An issue is what timezone is used for the dates which appear in the + diff output. rcsdiff uses the -z flag, which is not presently + processed by CVS diff, but I'm not sure exactly how hard to worry + about this--any such features are undocumented in the context of + CVS, and I'm not sure how important to users. */ +int +RCS_exec_rcsdiff (RCSNode *rcsfile, const char *opts, const char *options, + const char *rev1, const char *rev1_cache, const char *rev2, + const char *label1, const char *label2, const char *workfile) +{ + char *tmpfile1 = NULL; + char *tmpfile2 = NULL; + const char *use_file1, *use_file2; + int status, retval; + + + cvs_output ("\ +===================================================================\n\ +RCS file: ", 0); + cvs_output (rcsfile->path, 0); + cvs_output ("\n", 1); + + /* Historically, `cvs diff' has expanded the $Name keyword to the + empty string when checking out revisions. This is an accident, + but no one has considered the issue thoroughly enough to determine + what the best behavior is. Passing NULL for the `nametag' argument + preserves the existing behavior. */ + + cvs_output ("retrieving revision ", 0); + cvs_output (rev1, 0); + cvs_output ("\n", 1); + + if (rev1_cache != NULL) + use_file1 = rev1_cache; + else + { + tmpfile1 = cvs_temp_name(); + status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1, + (RCSCHECKOUTPROC)0, NULL); + if (status > 0) + { + retval = status; + goto error_return; + } + else if (status < 0) + { + error( 0, errno, + "cannot check out revision %s of %s", rev1, rcsfile->path ); + retval = 1; + goto error_return; + } + use_file1 = tmpfile1; + } + + if (rev2 == NULL) + { + assert (workfile != NULL); + use_file2 = workfile; + } + else + { + tmpfile2 = cvs_temp_name (); + cvs_output ("retrieving revision ", 0); + cvs_output (rev2, 0); + cvs_output ("\n", 1); + status = RCS_checkout (rcsfile, NULL, rev2, NULL, options, + tmpfile2, (RCSCHECKOUTPROC)0, NULL); + if (status > 0) + { + retval = status; + goto error_return; + } + else if (status < 0) + { + error (0, errno, + "cannot check out revision %s of %s", rev2, rcsfile->path); + return 1; + } + use_file2 = tmpfile2; + } + + RCS_output_diff_options (opts, rev1, rev2, workfile); + status = diff_exec( use_file1, use_file2, label1, label2, opts, RUN_TTY ); + if (status >= 0) + { + retval = status; + goto error_return; + } + else if (status < 0) + { + error (0, errno, + "cannot diff %s and %s", use_file1, use_file2); + retval = 1; + goto error_return; + } + + error_return: + { + /* Call CVS_UNLINK() below rather than unlink_file to avoid the check + * for noexec. + */ + if( tmpfile1 != NULL ) + { + if( CVS_UNLINK( tmpfile1 ) < 0 ) + { + if( !existence_error( errno ) ) + error( 0, errno, "cannot remove temp file %s", tmpfile1 ); + } + free( tmpfile1 ); + } + if( tmpfile2 != NULL ) + { + if( CVS_UNLINK( tmpfile2 ) < 0 ) + { + if( !existence_error( errno ) ) + error( 0, errno, "cannot remove temp file %s", tmpfile2 ); + } + free (tmpfile2); + } + } + + return retval; +} + + + +/* Show differences between two files. This is the start of a diff library. + + Some issues: + + * Should option parsing be part of the library or the caller? The + former allows the library to add options without changing the callers, + but it causes various problems. One is that something like --brief really + wants special handling in CVS, and probably the caller should retain + some flexibility in this area. Another is online help (the library could + have some feature for providing help, but how does that interact with + the help provided by the caller directly?). Another is that as things + stand currently, there is no separate namespace for diff options versus + "cvs diff" options like -l (that is, if the library adds an option which + conflicts with a CVS option, it is trouble). + + * This isn't required for a first-cut diff library, but if there + would be a way for the caller to specify the timestamps that appear + in the diffs (rather than the library getting them from the files), + that would clean up the kludgy utime() calls in patch.c. + + Show differences between FILE1 and FILE2. Either one can be + DEVNULL to indicate a nonexistent file (same as an empty file + currently, I suspect, but that may be an issue in and of itself). + OPTIONS is a list of diff options, or "" if none. At a minimum, + CVS expects that -c (update.c, patch.c) and -n (update.c) will be + supported. Other options, like -u, --speed-large-files, &c, will + be specified if the user specified them. + + OUT is a filename to send the diffs to, or RUN_TTY to send them to + stdout. Error messages go to stderr. Return value is 0 for + success, -1 for a failure which set errno, 1 for success (and some + differences were found), or >1 for a failure which printed a + message on stderr. */ + +int +diff_exec (const char *file1, const char *file2, const char *label1, + const char *label2, const char *options, const char *out) +{ + char *args; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If either file1 or file2 are special files, pretend they are + /dev/null. Reason: suppose a file that represents a block + special device in one revision becomes a regular file. CVS + must find the `difference' between these files, but a special + file contains no data useful for calculating this metric. The + safe thing to do is to treat the special file as an empty file, + thus recording the regular file's full contents. Doing so will + create extremely large deltas at the point of transition + between device files and regular files, but this is probably + very rare anyway. + + There may be ways around this, but I think they are fraught + with danger. -twp */ + + if (preserve_perms && + strcmp (file1, DEVNULL) != 0 && + strcmp (file2, DEVNULL) != 0) + { + struct stat sb1, sb2; + + if (CVS_LSTAT (file1, &sb1) < 0) + error (1, errno, "cannot get file information for %s", file1); + if (CVS_LSTAT (file2, &sb2) < 0) + error (1, errno, "cannot get file information for %s", file2); + + if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) + file1 = DEVNULL; + if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) + file2 = DEVNULL; + } +#endif + + args = xmalloc (strlen (options) + 10); + /* The first word in this string is used only for error reporting. */ + sprintf (args, "diff %s", options); + call_diff_setup (args); + if (label1) + call_diff_arg (label1); + if (label2) + call_diff_arg (label2); + call_diff_arg ("--"); + call_diff_arg (file1); + call_diff_arg (file2); + free (args); + + return call_diff (out); +} + +/* Print the options passed to DIFF, in the format used by rcsdiff. + The rcsdiff code that produces this output is extremely hairy, and + it is not clear how rcsdiff decides which options to print and + which not to print. The code below reproduces every rcsdiff run + that I have seen. */ + +static void +RCS_output_diff_options (const char *opts, const char *rev1, const char *rev2, + const char *workfile) +{ + char *tmp; + + tmp = (char *) xmalloc (strlen (opts) + strlen (rev1) + 10); + + sprintf (tmp, "diff%s -r%s", opts, rev1); + cvs_output (tmp, 0); + free (tmp); + + if (rev2) + { + cvs_output (" -r", 3); + cvs_output (rev2, 0); + } + else + { + assert (workfile != NULL); + cvs_output (" ", 1); + cvs_output (workfile, 0); + } + cvs_output ("\n", 1); +} diff --git a/contrib/cvs-1.12.9/src/recurse.c b/contrib/cvs-1.12.9/src/recurse.c new file mode 100644 index 0000000000..59d8c4e71c --- /dev/null +++ b/contrib/cvs-1.12.9/src/recurse.c @@ -0,0 +1,1347 @@ +/* + * Copyright (c) 1992, Brian Berliner and Jeff Polk + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * General recursion handler + * + */ + +#include "cvs.h" +#include "savecwd.h" +#include "fileattr.h" +#include "edit.h" + +static int do_dir_proc (Node * p, void *closure); +static int do_file_proc (Node * p, void *closure); +static void addlist (List ** listp, char *key); +static int unroll_files_proc (Node *p, void *closure); +static void addfile (List **listp, char *dir, char *file); + +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; + void *callerdat; + Dtype flags; + int which; + int aflag; + int locktype; + int dosrcs; + char *repository; /* Keep track of repository for rtag */ +}; + +static int do_recursion (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. + * + * INPUT + * + * fileproc + * Function called with each file as an argument. + * + * filesdoneproc + * Function called after all the files in a directory have been processed, + * before subdirectories have been processed. + * + * direntproc + * Function called immediately upon entering a directory, before processing + * any files or subdirectories. + * + * dirleaveproc + * Function called upon finishing a directory, immediately before leaving + * it and returning control to the function processing the parent + * directory. + * + * callerdat + * This void * is passed to the functions above. + * + * argc, argv + * The files on which to operate, interpreted as command line arguments. + * In the special case of no arguments, defaults to operating on the + * current directory (`.'). + * + * local + * + * which + * 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). + * + * aflag + * locktype + * update_preload + * dosrcs + * + * repository_in + * keeps track of the repository string. This is only for the remote mode, + * specifically, r* commands (rtag, rdiff, co, ...) where xgetwd() was used + * to locate the repository. Things would break when xgetwd() was used + * with a symlinked repository because xgetwd() would return the true path + * and in some cases this would cause the path to be printed as other than + * the user specified in error messages and in other cases some of CVS's + * security assertions would fail. + * + * GLOBALS + * ??? + * + * OUTPUT + * + * callerdat can be modified by the FILEPROC, FILESDONEPROC, DIRENTPROC, and + * DIRLEAVEPROC. + * + * RETURNS + * A count of errors counted by walking the argument list with + * unroll_files_proc() and do_recursion(). + * + * ERRORS + * Fatal errors occur: + * 1. when there were no arguments and the current directory + * does not contain CVS metadata. + * 2. when all but the last path element from an argument from ARGV cannot + * be found to be a local directory. + */ +int +start_recursion (FILEPROC fileproc, FILESDONEPROC filesdoneproc, + DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc, + void *callerdat, int argc, char **argv, int local, + int which, int aflag, int locktype, + char *update_preload, int dosrcs, char *repository_in) +{ + int i, err = 0; +#ifdef CLIENT_SUPPORT + List *args_to_send_when_finished = NULL; +#endif + List *files_by_dir = NULL; + struct recursion_frame frame; + +#ifdef HAVE_PRINTF_PTR + TRACE ( TRACE_FLOW, + "start_recursion ( fileproc=%p, filesdoneproc=%p,\n" + " direntproc=%p, dirleavproc=%p,\n" + " callerdat=%p, argc=%d, argv=%p,\n" + " local=%d, which=%d, aflag=%d,\n" + " locktype=%d, update_preload=%s\n" + " dosrcs=%d, repository_in=%s )", + (void *) fileproc, (void *) filesdoneproc, + (void *) direntproc, (void *) dirleaveproc, + (void *) callerdat, argc, (void *) argv, + local, which, aflag, locktype, + update_preload ? update_preload : "(null)", dosrcs, + repository_in ? repository_in : "(null)"); +#else + TRACE ( TRACE_FLOW, + "start_recursion ( fileproc=%lx, filesdoneproc=%lx,\n" + " direntproc=%lx, dirleavproc=%lx,\n" + " callerdat=%lx, argc=%d, argv=%lx,\n" + " local=%d, which=%d, aflag=%d,\n" + " locktype=%d, update_preload=%s\n" + " dosrcs=%d, repository_in=%s )", + (unsigned long) fileproc, (unsigned long) filesdoneproc, + (unsigned long) direntproc, (unsigned long) dirleaveproc, + (unsigned long) callerdat, argc, (unsigned long) argv, + local, which, aflag, locktype, + update_preload ? update_preload : "(null)", dosrcs, + repository_in ? repository_in : "(null)"); +#endif + + 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.locktype = locktype; + frame.dosrcs = dosrcs; + frame.repository = repository_in; + + expand_wild (argc, argv, &argc, &argv); + + if (update_preload == NULL) + update_dir = xstrdup (""); + else + update_dir = xstrdup (update_preload); + + /* clean up from any previous calls to start_recursion */ + if (repository) + { + free (repository); + repository = NULL; + } + if (filelist) + dellist (&filelist); /* FIXME-krp: no longer correct. */ + 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) + { + int just_subdirs = (which & W_LOCAL) && !isdir (CVSADM); + +#ifdef CLIENT_SUPPORT + if (!just_subdirs + && CVSroot_cmdline == NULL + && current_parsed_root->isremote) + { + char *root = Name_Root (NULL, update_dir); + if (root && strcmp (root, current_parsed_root->original) != 0) + /* We're skipping this directory because it is for + a different root. Therefore, we just want to + do the subdirectories only. Processing files would + cause a working directory from one repository to be + processed against a different repository, which could + cause all kinds of spurious conflicts and such. + + Question: what about the case of "cvs update foo" + where we process foo/bar and not foo itself? That + seems to be handled somewhere (else) but why should + it be a separate case? Needs investigation... */ + just_subdirs = 1; + free (root); + } +#endif + + /* + * There were no arguments, so we'll probably just recurse. The + * exception to the rule is when we are called from a directory + * without any CVS administration files. That has always meant to + * process each of the sub-directories, so we pretend like we were + * called with the list of sub-dirs of the current dir as args + */ + if (just_subdirs) + { + dirlist = Find_Directories (NULL, W_LOCAL, NULL); + /* If there are no sub-directories, there is a certain logic in + favor of doing nothing, but in fact probably the user is just + confused about what directory they are in, or whether they + cvs add'd a new directory. In the case of at least one + sub-directory, at least when we recurse into them we + notice (hopefully) whether they are under CVS control. */ + if (list_isempty (dirlist)) + { + if (update_dir[0] == '\0') + error (0, 0, "in directory .:"); + else + error (0, 0, "in directory %s:", update_dir); + error (1, 0, + "there is no version here; run '%s checkout' first", + program_name); + } +#ifdef CLIENT_SUPPORT + else if (current_parsed_root->isremote && server_started) + { + /* In the the case "cvs update foo bar baz", a call to + send_file_names in update.c will have sent the + appropriate "Argument" commands to the server. In + this case, that won't have happened, so we need to + do it here. While this example uses "update", this + generalizes to other commands. */ + + /* This is the same call to Find_Directories as above. + FIXME: perhaps it would be better to write a + function that duplicates a list. */ + args_to_send_when_finished = Find_Directories (NULL, + W_LOCAL, + NULL); + } +#endif + } + else + addlist (&dirlist, "."); + + goto do_the_work; + } + + + /* + * There were arguments, so we have to handle them by hand. To do + * that, we set up the filelist and dirlist with the arguments and + * call do_recursion. do_recursion recognizes the fact that the + * lists are non-null when it starts and doesn't update them. + * + * explicitly named directories are stored in dirlist. + * explicitly named files are stored in filelist. + * other possibility is named entities whicha are not currently in + * the working directory. + */ + + for (i = 0; i < argc; i++) + { + /* if this argument is a directory, then add it to the list of + directories. */ + + if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i])) + { + strip_trailing_slashes (argv[i]); + addlist (&dirlist, argv[i]); + } + else + { + /* otherwise, split argument into directory and component names. */ + char *dir; + char *comp; + char *file_to_try; + + /* Now break out argv[i] into directory part (DIR) and file part + * (COMP). DIR and COMP will each point to a newly malloc'd + * string. + */ + dir = xstrdup (argv[i]); + /* It's okay to cast out last_component's const below since we know + * we just allocated dir here and have control of it. + */ + comp = (char *)last_component (dir); + if (comp == dir) + { + /* no dir component. What we have is an implied "./" */ + dir = xstrdup("."); + } + else + { + comp[-1] = '\0'; + comp = xstrdup (comp); + } + + /* if this argument exists as a file in the current + working directory tree, then add it to the files list. */ + + if (!(which & W_LOCAL)) + { + /* If doing rtag, we've done a chdir to the repository. */ + file_to_try = xmalloc( strlen( argv[i] ) + + sizeof( RCSEXT ) + 1 ); + sprintf (file_to_try, "%s%s", argv[i], RCSEXT); + } + else + file_to_try = xstrdup (argv[i]); + + if (isfile (file_to_try)) + addfile (&files_by_dir, dir, comp); + else if (isdir (dir)) + { + if ((which & W_LOCAL) && isdir (CVSADM) +#ifdef CLIENT_SUPPORT + && !current_parsed_root->isremote +#endif + ) + { + /* otherwise, look for it in the repository. */ + char *tmp_update_dir; + char *repos; + char *reposfile; + + tmp_update_dir = xmalloc (strlen (update_dir) + + strlen (dir) + + 5); + strcpy (tmp_update_dir, update_dir); + + if (*tmp_update_dir != '\0') + strcat (tmp_update_dir, "/"); + + strcat (tmp_update_dir, dir); + + /* look for it in the repository. */ + repos = Name_Repository (dir, tmp_update_dir); + reposfile = xmalloc (strlen (repos) + + strlen (comp) + + 5); + sprintf (reposfile, "%s/%s", repos, comp); + free (repos); + + if (!wrap_name_has (comp, WRAP_TOCVS) && isdir (reposfile)) + addlist (&dirlist, argv[i]); + else + addfile (&files_by_dir, dir, comp); + + free (tmp_update_dir); + free (reposfile); + } + else + addfile (&files_by_dir, dir, comp); + } + else + error (1, 0, "no such directory `%s'", dir); + + free (file_to_try); + free (dir); + free (comp); + } + } + + /* At this point we have looped over all named arguments and built + a coupla lists. Now we unroll the lists, setting up and + calling do_recursion. */ + + err += walklist (files_by_dir, unroll_files_proc, (void *) &frame); + dellist(&files_by_dir); + + /* then do_recursion on the dirlist. */ + if (dirlist != NULL) + { + do_the_work: + err += do_recursion (&frame); + } + + /* Free the data which expand_wild allocated. */ + free_names (&argc, argv); + + free (update_dir); + update_dir = NULL; + +#ifdef CLIENT_SUPPORT + if (args_to_send_when_finished != NULL) + { + /* FIXME (njc): in the multiroot case, we don't want to send + argument commands for those top-level directories which do + not contain any subdirectories which have files checked out + from current_parsed_root->original. If we do, and two repositories + have a module with the same name, nasty things could happen. + + This is hard. Perhaps we should send the Argument commands + later in this procedure, after we've had a chance to notice + which directores we're using (after do_recursion has been + called once). This means a _lot_ of rewriting, however. + + What we need to do for that to happen is descend the tree + and construct a list of directories which are checked out + from current_cvsroot. Now, we eliminate from the list all + of those directories which are immediate subdirectories of + another directory in the list. To say that the opposite + way, we keep the directories which are not immediate + subdirectories of any other in the list. Here's a picture: + + a + / \ + B C + / \ + D e + / \ + F G + / \ + H I + + The node in capitals are those directories which are + checked out from current_cvsroot. We want the list to + contain B, C, F, and G. D, H, and I are not included, + because their parents are also checked out from + current_cvsroot. + + The algorithm should be: + + 1) construct a tree of all directory names where each + element contains a directory name and a flag which notes if + that directory is checked out from current_cvsroot + + a0 + / \ + B1 C1 + / \ + D1 e0 + / \ + F1 G1 + / \ + H1 I1 + + 2) Recursively descend the tree. For each node, recurse + before processing the node. If the flag is zero, do + nothing. If the flag is 1, check the node's parent. If + the parent's flag is one, change the current entry's flag + to zero. + + a0 + / \ + B1 C1 + / \ + D0 e0 + / \ + F1 G1 + / \ + H0 I0 + + 3) Walk the tree and spit out "Argument" commands to tell + the server which directories to munge. + + Yuck. It's not clear this is worth spending time on, since + we might want to disable cvs commands entirely from + directories that do not have CVSADM files... + + Anyways, the solution as it stands has modified server.c + (dirswitch) to create admin files [via server.c + (create_adm_p)] in all path elements for a client's + "Directory xxx" command, which forces the server to descend + and serve the files there. client.c (send_file_names) has + also been modified to send only those arguments which are + appropriate to current_parsed_root->original. + + */ + + /* Construct a fake argc/argv pair. */ + + int our_argc = 0, i; + char **our_argv = NULL; + + if (! list_isempty (args_to_send_when_finished)) + { + Node *head, *p; + + head = args_to_send_when_finished->list; + + /* count the number of nodes */ + i = 0; + for (p = head->next; p != head; p = p->next) + i++; + our_argc = i; + + /* create the argument vector */ + our_argv = (char **) xmalloc (sizeof (char *) * our_argc); + + /* populate it */ + i = 0; + for (p = head->next; p != head; p = p->next) + our_argv[i++] = xstrdup (p->key); + } + + /* We don't want to expand widcards, since we've just created + a list of directories directly from the filesystem. */ + send_file_names (our_argc, our_argv, 0); + + /* Free our argc/argv. */ + if (our_argv != NULL) + { + for (i = 0; i < our_argc; i++) + free (our_argv[i]); + free (our_argv); + } + + dellist (&args_to_send_when_finished); + } +#endif + + return (err); +} + + + +/* + * Implement the recursive policies on the local directory. This may be + * called directly, or may be called by start_recursion. + */ +static int +do_recursion (struct recursion_frame *frame) +{ + int err = 0; + int dodoneproc = 1; + char *srepository = NULL; + List *entries = NULL; + int locktype; + int process_this_directory = 1; + +#ifdef HAVE_PRINT_PTR + TRACE (TRACE_FLOW, "do_recursion ( frame=%p )", (void *) frame); +#else + TRACE (TRACE_FLOW, "do_recursion ( frame=%lx )", (unsigned long) frame); +#endif + + /* do nothing if told */ + if (frame->flags == R_SKIP_ALL) + return 0; + + locktype = noexec ? CVS_LOCK_NONE : frame->locktype; + + /* The fact that locks are not active here is what makes us fail to have + the + + If someone commits some changes in one cvs command, + then an update by someone else will either get all the + changes, or none of them. + + property (see node Concurrency in cvs.texinfo). + + The most straightforward fix would just to readlock the whole + tree before starting an update, but that means that if a commit + gets blocked on a big update, it might need to wait a *long* + time. + + A more adequate fix would be a two-pass design for update, + checkout, etc. The first pass would go through the repository, + with the whole tree readlocked, noting what versions of each + file we want to get. The second pass would release all locks + (except perhaps short-term locks on one file at a + time--although I think RCS already deals with this) and + actually get the files, specifying the particular versions it wants. + + This could be sped up by separating out the data needed for the + first pass into a separate file(s)--for example a file + attribute for each file whose value contains the head revision + for each branch. The structure should be designed so that + commit can relatively quickly update the information for a + single file or a handful of files (file attributes, as + implemented in Jan 96, are probably acceptable; improvements + would be possible such as branch attributes which are in + separate files for each branch). */ + +#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL) + /* + * Now would be a good time to check to see if we need to stop + * generating data, to give the buffers a chance to drain to the + * remote client. We should not have locks active at this point, + * but if there are writelocks around, we cannot pause here. */ + if (server_active && locktype != CVS_LOCK_NONE) + server_pause_check(); +#endif + + /* Check the value in CVSADM_ROOT and see if it's in the list. If + not, add it to our lists of CVS/Root directories and do not + process the files in this directory. Otherwise, continue as + usual. THIS_ROOT might be NULL if we're doing an initial + checkout -- check before using it. The default should be that + we process a directory's contents and only skip those contents + if a CVS/Root file exists. + + If we're running the server, we want to process all + directories, since we're guaranteed to have only one CVSROOT -- + our own. */ + + if ( + /* If -d was specified, it should override CVS/Root. + + In the single-repository case, it is long-standing CVS behavior + and makes sense - the user might want another access method, + another server (which mounts the same repository), &c. + + In the multiple-repository case, -d overrides all CVS/Root + files. That is the only plausible generalization I can + think of. */ + CVSroot_cmdline == NULL + +#ifdef SERVER_SUPPORT + && ! server_active +#endif + ) + { + char *this_root = Name_Root (NULL, update_dir); + if (this_root != NULL) + { + if (findnode (root_directories, this_root) == NULL) + { + /* Add it to our list. */ + + Node *n = getnode (); + n->type = NT_UNKNOWN; + n->key = xstrdup (this_root); + + if (addnode (root_directories, n)) + error (1, 0, "cannot add new CVSROOT %s", this_root); + + } + + process_this_directory = + (strcmp (current_parsed_root->original, this_root) == 0); + + free (this_root); + } + } + + /* + * Fill in repository with the current repository + */ + if (frame->which & W_LOCAL) + { + if (isdir (CVSADM)) + { + repository = Name_Repository (NULL, update_dir); + srepository = repository; /* remember what to free */ + } + else + repository = NULL; + } + else + { + repository = frame->repository; + assert (repository != NULL); + assert (strstr (repository, "/./") == NULL); + } + + fileattr_startdir (repository); + + /* + * The filesdoneproc needs to be called for each directory where files + * processed, or each directory that is processed by a call where no + * directories were passed in. In fact, the only time we don't want to + * call back the filesdoneproc is when we are processing directories that + * were passed in on the command line (or in the special case of `.' when + * we were called with no args + */ + if (dirlist != NULL && filelist == NULL) + dodoneproc = 0; + + /* + * If filelist or dirlist is already set, we don't look again. Otherwise, + * find the files and directories + */ + if (filelist == NULL && dirlist == NULL) + { + /* both lists were NULL, so start from scratch */ + if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) + { + 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) + { + Name_Repository (NULL, update_dir); + assert (!"Not reached. Please report this problem to "); + } + /* find the files and fill in entries if appropriate */ + if (process_this_directory) + { + filelist = Find_Names (repository, lwhich, frame->aflag, + &entries); + if (filelist == NULL) + { + error (0, 0, "skipping directory %s", update_dir); + /* Note that Find_Directories and the filesdoneproc + in particular would do bad things ("? foo.c" in + the case of some filesdoneproc's). */ + goto skip_directory; + } + } + } + + /* find sub-directories if we will recurse */ + if (frame->flags != R_SKIP_DIRS) + dirlist = Find_Directories ( + process_this_directory ? repository : NULL, + frame->which, entries); + } + else + { + /* something was passed on the command line */ + if (filelist != NULL && frame->fileproc != NULL) + { + /* we will process files, so pre-parse entries */ + if (frame->which & W_LOCAL) + entries = Entries_Open (frame->aflag, NULL); + } + } + + /* process the files (if any) */ + if (process_this_directory && filelist != NULL && frame->fileproc) + { + struct file_info finfo_struct; + struct frame_and_file frfile; + + /* Lock the repository, if necessary. */ + if (repository) + { + if (locktype == CVS_LOCK_READ) + { + if (Reader_Lock (repository) != 0) + error (1, 0, "read lock failed - giving up"); + } + else if (locktype == CVS_LOCK_WRITE) + lock_dir_for_write (repository); + } + +#ifdef CLIENT_SUPPORT + /* For the server, we handle notifications in a completely different + place (server_notify). For local, we can't do them here--we don't + have writelocks in place, and there is no way to get writelocks + here. */ + if (current_parsed_root->isremote) + notify_check (repository, update_dir); +#endif /* CLIENT_SUPPORT */ + + finfo_struct.repository = repository; + finfo_struct.update_dir = update_dir; + 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, &frfile); + + /* unlock it */ + if (/* We only lock the repository above when repository is set */ + repository + /* and when asked for a read or write lock. */ + && locktype != CVS_LOCK_NONE) + Simple_Lock_Cleanup (); + + /* clean up */ + dellist (&filelist); + } + + /* call-back files done proc (if any) */ + if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL) + err = frame->filesdoneproc (frame->callerdat, err, repository, + update_dir[0] ? update_dir : ".", + entries); + + skip_directory: + fileattr_write (); + fileattr_free (); + + /* process the directories (if necessary) */ + if (dirlist != NULL) + { + struct frame_and_entries frent; + + frent.frame = frame; + frent.entries = entries; + err += walklist (dirlist, do_dir_proc, &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) + { + free (srepository); + } + repository = NULL; + +#ifdef HAVE_PRINT_PTR + TRACE (TRACE_FLOW, "Leaving do_recursion ( frame=%p )", (void *) frame); +#else + TRACE (TRACE_FLOW, "Leaving do_recursion ( frame=%lx )", (unsigned long) frame); +#endif + + return err; +} + + + +/* + * Process each of the files in the list with the callback proc + */ +static int +do_file_proc (Node *p, void *closure) +{ + struct frame_and_file *frfile = (struct frame_and_file *)closure; + struct file_info *finfo = frfile->finfo; + int ret; + char *tmp; + + finfo->file = p->key; + tmp = xmalloc (strlen (finfo->file) + + strlen (finfo->update_dir) + + 2); + tmp[0] = '\0'; + if (finfo->update_dir[0] != '\0') + { + strcat (tmp, finfo->update_dir); + strcat (tmp, "/"); + } + strcat (tmp, finfo->file); + + if (frfile->frame->dosrcs && repository) + { + finfo->rcs = RCS_parse (finfo->file, repository); + + /* OK, without W_LOCAL the error handling becomes relatively + simple. The file names came from readdir() on the + repository and so we know any ENOENT is an error + (e.g. symlink pointing to nothing). Now, the logic could + be simpler - since we got the name from readdir, we could + just be calling RCS_parsercsfile. */ + if (finfo->rcs == NULL + && !(frfile->frame->which & W_LOCAL)) + { + error (0, 0, "could not read RCS file for %s", tmp); + free (tmp); + cvs_flushout (); + return 0; + } + } + else + finfo->rcs = (RCSNode *) NULL; + finfo->fullname = tmp; + ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); + + freercsnode(&finfo->rcs); + free (tmp); + + /* 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; +} + + + +/* + * Process each of the directories in the list (recursing as we go) + */ +static int +do_dir_proc (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; + List *sdirlist; + char *srepository; + Dtype dir_return = R_PROCESS; + int stripped_dot = 0; + int err = 0; + struct saved_cwd cwd; + char *saved_update_dir; + int process_this_directory = 1; + + 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) + { + if (update_dir[0] != '\0') + { + (void) strcat (update_dir, "/"); + (void) strcat (update_dir, dir); + } + else + (void) strcpy (update_dir, dir); + + /* + * Here we need a plausible repository name for the sub-directory. We + * create one by concatenating the new directory name onto the + * previous repository name. The only case where the name should be + * used is in the case where we are creating a new sub-directory for + * update -d and in that case the generated name will be correct. + */ + if (repository == NULL) + newrepos = xstrdup (""); + else + { + newrepos = xmalloc (strlen (repository) + strlen (dir) + 5); + sprintf (newrepos, "%s/%s", repository, dir); + } + } + else + { + if (update_dir[0] == '\0') + (void) strcpy (update_dir, dir); + + if (repository == NULL) + newrepos = xstrdup (""); + else + newrepos = xstrdup (repository); + } + + /* Check to see that the CVSADM directory, if it exists, seems to be + well-formed. It can be missing files if the user hit ^C in the + middle of a previous run. We want to (a) make this a nonfatal + error, and (b) make sure we print which directory has the + problem. + + Do this before the direntproc, so that (1) the direntproc + doesn't have to guess/deduce whether we will skip the directory + (e.g. send_dirent_proc and whether to send the directory), and + (2) so that the warm fuzzy doesn't get printed if we skip the + directory. */ + if (frame->which & W_LOCAL) + { + char *cvsadmdir; + + cvsadmdir = xmalloc (strlen (dir) + + sizeof (CVSADM_REP) + + sizeof (CVSADM_ENT) + + 80); + + strcpy (cvsadmdir, dir); + strcat (cvsadmdir, "/"); + strcat (cvsadmdir, CVSADM); + if (isdir (cvsadmdir)) + { + strcpy (cvsadmdir, dir); + strcat (cvsadmdir, "/"); + strcat (cvsadmdir, CVSADM_REP); + if (!isfile (cvsadmdir)) + { + /* Some commands like update may have printed "? foo" but + if we were planning to recurse, and don't on account of + CVS/Repository, we want to say why. */ + error (0, 0, "ignoring %s (%s missing)", update_dir, + CVSADM_REP); + dir_return = R_SKIP_ALL; + } + + /* Likewise for CVS/Entries. */ + if (dir_return != R_SKIP_ALL) + { + strcpy (cvsadmdir, dir); + strcat (cvsadmdir, "/"); + strcat (cvsadmdir, CVSADM_ENT); + if (!isfile (cvsadmdir)) + { + /* Some commands like update may have printed "? foo" but + if we were planning to recurse, and don't on account of + CVS/Repository, we want to say why. */ + error (0, 0, "ignoring %s (%s missing)", update_dir, + CVSADM_ENT); + dir_return = R_SKIP_ALL; + } + } + } + free (cvsadmdir); + } + + /* Only process this directory if the root matches. This nearly + duplicates code in do_recursion. */ + + if ( + /* If -d was specified, it should override CVS/Root. + + In the single-repository case, it is long-standing CVS behavior + and makes sense - the user might want another access method, + another server (which mounts the same repository), &c. + + In the multiple-repository case, -d overrides all CVS/Root + files. That is the only plausible generalization I can + think of. */ + CVSroot_cmdline == NULL + +#ifdef SERVER_SUPPORT + && ! server_active +#endif + ) + { + char *this_root = Name_Root (dir, update_dir); + if (this_root != NULL) + { + if (findnode (root_directories, this_root) == NULL) + { + /* Add it to our list. */ + + Node *n = getnode (); + n->type = NT_UNKNOWN; + n->key = xstrdup (this_root); + + if (addnode (root_directories, n)) + error (1, 0, "cannot add new CVSROOT %s", this_root); + + } + + process_this_directory = (strcmp (current_parsed_root->original, this_root) == 0); + + free (this_root); + } + } + + /* call-back dir entry proc (if any) */ + if (dir_return == R_SKIP_ALL) + ; + else if (frame->direntproc != NULL) + { + /* If we're doing the actual processing, call direntproc. + Otherwise, assume that we need to process this directory + and recurse. FIXME. */ + + if (process_this_directory) + dir_return = frame->direntproc (frame->callerdat, dir, newrepos, + update_dir, frent->entries); + else + dir_return = R_PROCESS; + } + else + { + /* Generic behavior. I don't see a reason to make the caller specify + a direntproc just to get this. */ + if ((frame->which & W_LOCAL) && !isdir (dir)) + dir_return = R_SKIP_ALL; + } + + 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); + sdirlist = dirlist; + srepository = repository; + dirlist = NULL; + + /* cd to the sub-directory */ + if (CVS_CHDIR (dir) < 0) + error (1, errno, "could not chdir to %s", dir); + + /* honor the global SKIP_DIRS (a.k.a. local) */ + if (frame->flags == R_SKIP_DIRS) + dir_return = R_SKIP_DIRS; + + /* remember if the `.' will be stripped for subsequent dirs */ + if (strcmp (update_dir, ".") == 0) + { + update_dir[0] = '\0'; + stripped_dot = 1; + } + + /* make the recursive call */ + xframe = *frame; + xframe.flags = dir_return; + /* Keep track of repository, really just for r* commands (rtag, rdiff, + * co, ...) to tag_check_valid, since all the other commands use + * CVS/Repository to figure it out per directory. + */ + if (repository) + { + if (strcmp (dir, ".") == 0) + xframe.repository = xstrdup (repository); + else + { + xframe.repository = xmalloc (strlen (repository) + + strlen (dir) + + 2); + sprintf (xframe.repository, "%s/%s", repository, dir); + } + } + else + xframe.repository = NULL; + err += do_recursion (&xframe); + if (xframe.repository) + { + free (xframe.repository); + xframe.repository = NULL; + } + + /* put the `.' back if necessary */ + if (stripped_dot) + (void) strcpy (update_dir, "."); + + /* call-back dir leave proc (if any) */ + if (process_this_directory && 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); + free_cwd (&cwd); + dirlist = sdirlist; + repository = srepository; + } + + free (update_dir); + update_dir = saved_update_dir; + + return err; +} + +/* + * Add a node to a list allocating the list if necessary. + */ +static void +addlist (List **listp, char *key) +{ + Node *p; + + if (*listp == NULL) + *listp = getlist (); + p = getnode (); + p->type = FILES; + p->key = xstrdup (key); + if (addnode (*listp, p) != 0) + freenode (p); +} + +static void +addfile (List **listp, char *dir, char *file) +{ + Node *n; + List *fl; + + /* add this dir. */ + addlist (listp, dir); + + n = findnode (*listp, dir); + if (n == NULL) + { + error (1, 0, "can't find recently added dir node `%s' in start_recursion.", + dir); + } + + n->type = DIRS; + fl = n->data; + addlist (&fl, file); + n->data = fl; + return; +} + +static int +unroll_files_proc (Node *p, void *closure) +{ + Node *n; + struct recursion_frame *frame = (struct recursion_frame *) closure; + int err = 0; + List *save_dirlist; + char *save_update_dir = NULL; + struct saved_cwd cwd; + + /* if this dir was also an explicitly named argument, then skip + it. We'll catch it later when we do dirs. */ + n = findnode (dirlist, p->key); + if (n != NULL) + return (0); + + /* otherwise, call dorecusion for this list of files. */ + filelist = p->data; + p->data = NULL; + save_dirlist = dirlist; + dirlist = NULL; + + if (strcmp(p->key, ".") != 0) + { + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + if ( CVS_CHDIR (p->key) < 0) + error (1, errno, "could not chdir to %s", p->key); + + 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, "/"); + + (void) strcat (update_dir, p->key); + } + + err += do_recursion (frame); + + if (save_update_dir != NULL) + { + free (update_dir); + update_dir = save_update_dir; + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + } + + dirlist = save_dirlist; + if (filelist) + dellist (&filelist); + return(err); +} +/* vim:tabstop=8:shiftwidth=4 + */ diff --git a/contrib/cvs-1.12.9/src/release.c b/contrib/cvs-1.12.9/src/release.c new file mode 100644 index 0000000000..f50fb84752 --- /dev/null +++ b/contrib/cvs-1.12.9/src/release.c @@ -0,0 +1,317 @@ +/* + * Release: "cancel" a checkout in the history log. + * + * - Enter a line in the history log indicating the "release". - If asked to, + * delete the local working directory. + */ + +#include "cvs.h" +#include "savecwd.h" +#include "getline.h" + +static const char *const release_usage[] = +{ + "Usage: %s %s [-d] directories...\n", + "\t-d\tDelete the given directory.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +#ifdef SERVER_SUPPORT +static int release_server (int argc, char **argv); + +/* This is the server side of cvs release. */ +static int +release_server (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 */ + +/* There are various things to improve about this implementation: + + 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. In particular, there is + no particularly good way to find the right "cvs". + + 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. (One + idea for how to fix this is to stash a copy of CVSROOTADM_IGNORE in + the working directories; see comment at base_* in entries.c for a + few thoughts on that). + + 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. 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 (int argc, char **argv) +{ + FILE *fp; + 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; + struct saved_cwd cwd; + +#ifdef SERVER_SUPPORT + if (server_active) + return release_server (argc, argv); +#endif + + /* Everything from here on is client or local. */ + if (argc == -1) + usage (release_usage); + optind = 0; + while ((c = getopt (argc, argv, "+Qdq")) != -1) + { + switch (c) + { + case 'Q': + case 'q': + error (1, 0, + "-q or -Q must be specified before \"%s\"", + cvs_cmd_name); + break; + case 'd': + delete_flag++; + break; + case '?': + default: + usage (release_usage); + break; + } + } + 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. + * (Well, actually we ask no matter what. Our notion of "sufficiently + * unalarming" doesn't take into account "? foo.c" files, so it is + * up to the user to take note of them, at least currently + * (ignore-193 in testsuite)). + */ + /* Construct the update command. Be sure to add authentication and + encryption if we are using them currently, else our child process may + not be able to communicate with the server. */ + update_cmd = xmalloc (strlen (program_path) + + strlen (current_parsed_root->original) + + 1 + 3 + 3 + 16 + 1); + sprintf (update_cmd, "%s %s%s-n -q -d %s update", + program_path, + cvsauthenticate ? "-a " : "", + cvsencrypt ? "-x " : "", + current_parsed_root->original); + +#ifdef CLIENT_SUPPORT + /* Start the server; we'll close it after looping. */ + if (current_parsed_root->isremote) + { + start_server (); + ign_setup (); + } +#endif /* CLIENT_SUPPORT */ + + /* Remember the directory where "cvs release" was invoked because + all args are relative to this directory and we chdir around. + */ + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + + arg_start_idx = 0; + + for (i = arg_start_idx; i < argc; i++) + { + thisarg = argv[i]; + + if (isdir (thisarg)) + { + 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); + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + continue; + } + } + else + { + if (!really_quiet) + error (0, 0, "no such directory: %s", thisarg); + continue; + } + + repository = Name_Repository ((char *) NULL, (char *) NULL); + + if (!really_quiet) + { + int line_length; + + /* 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"); + if (fp == NULL) + error (1, 0, "cannot run command %s", update_cmd); + + c = 0; + + while ((line_length = getline (&line, &line_allocated, fp)) >= 0) + { + if (strchr ("MARCZ", *line)) + c++; + (void) fputs (line, stdout); + } + if (line_length < 0 && !feof (fp)) + error (0, errno, "cannot read from subprocess"); + + /* 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); + free (repository); + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + 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", + cvs_cmd_name); + free (repository); + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + continue; + } + } + + /* Note: client.c doesn't like to have other code + changing the current directory on it. So a fair amount + of effort is needed to make sure it doesn't get confused + about the directory and (for example) overwrite + CVS/Entries file in the wrong directory. See release-17 + through release-23. */ + + free (repository); + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + + if (1 +#ifdef CLIENT_SUPPORT + && !(current_parsed_root->isremote + && (!supported_request ("noop") + || !supported_request ("Notify"))) +#endif + ) + { + int argc = 2; + char *argv[3]; + argv[0] = "dummy"; + argv[1] = thisarg; + argv[2] = NULL; + err += unedit (argc, argv); + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + } + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + 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 */ + } + + if (delete_flag) + { + /* FIXME? Shouldn't this just delete the CVS-controlled + files and, perhaps, the files that would normally be + ignored and leave everything else? */ + + if (unlink_file_dir (thisarg) < 0) + error (0, errno, "deletion of directory %s failed", thisarg); + } + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + /* FIXME: + * Is there a good reason why get_server_responses() isn't + * responsible for restoring its initial directory itself when + * finished? + */ + err += get_server_responses (); + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + } +#endif /* CLIENT_SUPPORT */ + } + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + /* 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; +} diff --git a/contrib/cvs-1.12.9/src/remove.c b/contrib/cvs-1.12.9/src/remove.c new file mode 100644 index 0000000000..b44847917e --- /dev/null +++ b/contrib/cvs-1.12.9/src/remove.c @@ -0,0 +1,281 @@ +/* + * 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 source distribution. + * + * Remove a File + * + * Removes entries from the present version. The entries will be removed from + * the RCS repository upon the next "commit". + * + * "remove" accepts no options, only file names that are to be removed. The + * file must not exist in the current directory for "remove" to work + * correctly. + */ + +#include "cvs.h" + +#ifdef CLIENT_SUPPORT +static int remove_force_fileproc (void *callerdat, + struct file_info *finfo); +#endif +static int remove_fileproc (void *callerdat, struct file_info *finfo); +static Dtype remove_dirproc (void *callerdat, const char *dir, + const char *repos, const char *update_dir, + List *entries); + +static int force; +static int local; +static int removed_files; +static int existing_files; + +static const char *const remove_usage[] = +{ + "Usage: %s %s [-flR] [files...]\n", + "\t-f\tDelete the file before removing it.\n", + "\t-l\tProcess this directory only (not recursive).\n", + "\t-R\tProcess directories recursively.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +cvsremove (int argc, char **argv) +{ + int c, err; + + if (argc == -1) + usage (remove_usage); + + optind = 0; + while ((c = getopt (argc, argv, "+flR")) != -1) + { + switch (c) + { + case 'f': + force = 1; + break; + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case '?': + default: + usage (remove_usage); + break; + } + } + argc -= optind; + argv += optind; + + wrap_setup (); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) { + /* 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, + NULL, argc, argv, local, W_LOCAL, + 0, CVS_LOCK_NONE, (char *) NULL, 0, (char *) NULL ); + } + /* 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_arg ("--"); + /* FIXME: Can't we set SEND_NO_CONTENTS here? Needs investigation. */ + send_files (argc, argv, local, 0, 0); + send_file_names (argc, argv, 0); + free_names (&argc, argv); + send_to_server ("remove\012", 0); + return get_responses_and_close (); + } +#endif + + /* start the recursion processor */ + err = start_recursion + ( remove_fileproc, (FILESDONEPROC) NULL, + remove_dirproc, (DIRLEAVEPROC) NULL, NULL, argc, argv, + local, W_LOCAL, 0, CVS_LOCK_READ, (char *) NULL, 1, + (char *) NULL ); + + if (removed_files && !really_quiet) + error (0, 0, "use `%s commit' to remove %s permanently", program_name, + (removed_files == 1) ? "this file" : "these files"); + + if (existing_files) + error (0, 0, + ((existing_files == 1) ? + "%d file exists; remove it first" : + "%d files exist; remove them first"), + existing_files); + + 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 (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 (void *callerdat, struct file_info *finfo) +{ + Vers_TS *vers; + + if (force) + { + if (!noexec) + { + if ( CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno)) + { + error (0, errno, "unable to remove %s", finfo->fullname); + } + } + /* else FIXME should probably act as if the file doesn't exist + in doing the following checks. */ + } + + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); + + if (vers->ts_user != NULL) + { + existing_files++; + if (!quiet) + error (0, 0, "file `%s' still in working directory", + finfo->fullname); + } + else if (vers->vn_user == NULL) + { + if (!quiet) + error (0, 0, "nothing known about `%s'", finfo->fullname); + } + 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); + if (unlink_file (fname) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", CVSEXT_LOG); + if (!quiet) + error (0, 0, "removed `%s'", finfo->fullname); + +#ifdef SERVER_SUPPORT + if (server_active) + server_checked_in (finfo->file, finfo->update_dir, finfo->repository); +#endif + free (fname); + } + else if (vers->vn_user[0] == '-') + { + if (!quiet) + error (0, 0, "file `%s' already scheduled for removal", + finfo->fullname); + } + else if (vers->tag != NULL && isdigit ((unsigned char) *vers->tag)) + { + /* Commit will just give an error, and so there seems to be + little reason to allow the remove. I mean, conflicts that + arise out of parallel development are one thing, but conflicts + that arise from sticky tags are quite another. + + I would have thought that non-branch sticky tags should be the + same but at least now, removing a file with a non-branch sticky + tag means to delete the tag from the file. I'm not sure that + is a good behavior, but until it is changed, we need to allow + it. */ + error (0, 0, "\ +cannot remove file `%s' which has a numeric sticky tag of `%s'", + finfo->fullname, vers->tag); + } + else if (vers->date != NULL) + { + /* Commit will just give an error, and so there seems to be + little reason to allow the remove. */ + error (0, 0, "\ +cannot remove file `%s' which has a sticky date of `%s'", + finfo->fullname, vers->date); + } + 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, + vers->tag, vers->date, vers->ts_conflict); + if (!quiet) + error (0, 0, "scheduling `%s' for removal", finfo->fullname); + removed_files++; + +#ifdef SERVER_SUPPORT + if (server_active) + server_checked_in (finfo->file, finfo->update_dir, finfo->repository); +#endif + free (fname); + } + + freevers_ts (&vers); + return (0); +} + + + +/* + * Print a warm fuzzy message + */ +/* ARGSUSED */ +static Dtype +remove_dirproc (void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries) +{ + if (!quiet) + error (0, 0, "Removing %s", update_dir); + return (R_PROCESS); +} diff --git a/contrib/cvs-1.12.9/src/repos.c b/contrib/cvs-1.12.9/src/repos.c new file mode 100644 index 0000000000..5bf6e5b2c1 --- /dev/null +++ b/contrib/cvs-1.12.9/src/repos.c @@ -0,0 +1,202 @@ +/* + * 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 source distribution. + */ + +#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 + itself if DIR is NULL. Returns the name in a newly-malloc'd + string. On error, gives a fatal error and does not return. + UPDATE_DIR is the path from where cvs was invoked (for use in error + messages), and should contain DIR as its last component. + UPDATE_DIR can be NULL to signify the directory in which cvs was + invoked. */ + +char * +Name_Repository (const char *dir, const char *update_dir) +{ + FILE *fpin; + const char *xupdate_dir; + char *repos = NULL; + size_t repos_allocated = 0; + char *tmp; + char *cp; + + if (update_dir && *update_dir) + xupdate_dir = update_dir; + else + xupdate_dir = "."; + + if (dir != NULL) + { + tmp = xmalloc (strlen (dir) + sizeof (CVSADM_REP) + 10); + (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP); + } + else + tmp = xstrdup (CVSADM_REP); + + /* + * The assumption here is that the repository is always contained in the + * first line of the "Repository" file. + */ + fpin = CVS_FOPEN (tmp, "r"); + + 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)) + { + /* This occurs at least in the case where the user manually + * creates a directory named CVS. + */ + error (0, 0, "in directory `%s':", xupdate_dir); + error (0, 0, "CVS directory found without administrative files."); + error (0, 0, "Use CVS to create the CVS directory, or rename the"); + error (0, 0, "directory if it is intended to store something"); + error (0, 0, "besides CVS administrative files."); + error (1, 0, "*PANIC* administration files missing!"); + } + + error (1, save_errno, "cannot open `%s'", 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); + } + if (fclose (fpin) < 0) + error (0, errno, "cannot close `%s'", tmp); + free (tmp); + + if ((cp = strrchr (repos, '\n')) != NULL) + *cp = '\0'; /* strip the newline */ + + /* + * If this is a relative repository pathname, turn it into an absolute + * one by tacking on the CVSROOT environment variable. If the CVSROOT + * environment variable is not set, die now. + */ + if (! isabsolute(repos)) + { + char *newrepos; + + if (current_parsed_root == 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, "invalid repository setting"); + } + if (pathname_levels (repos) > 0) + { + error (0, 0, "in directory `%s':", xupdate_dir); + error (0, 0, "`..'-relative repositories are not supported."); + error (1, 0, "invalid source repository"); + } + newrepos = xmalloc (strlen (current_parsed_root->directory) + + strlen (repos) + 2); + sprintf (newrepos, "%s/%s", current_parsed_root->directory, repos); + free (repos); + repos = newrepos; + } + + Sanitize_Repository_Name (repos); + + return repos; +} + + + +/* + * Return a pointer to the repository name relative to CVSROOT from a + * possibly fully qualified repository + */ +const char * +Short_Repository (const char *repository) +{ + if (repository == NULL) + return NULL; + + /* If repository matches CVSroot at the beginning, strip off CVSroot */ + /* And skip leading '/' in rep, in case CVSroot ended with '/'. */ + if (strncmp (current_parsed_root->directory, repository, + strlen (current_parsed_root->directory)) == 0) + { + const char *rep = repository + strlen (current_parsed_root->directory); + return (*rep == '/') ? rep+1 : rep; + } + else + return repository; +} + + + +/* Sanitize the repository name (in place) by removing trailing + * slashes and a trailing "." if present. It should be safe for + * callers to use strcat and friends to create repository names. + * Without this check, names like "/path/to/repos/./foo" and + * "/path/to/repos//foo" would be created. For example, one + * significant case is the CVSROOT-detection code in commit.c. It + * decides whether or not it needs to rebuild the administrative file + * database by doing a string compare. If we've done a `cvs co .' to + * get the CVSROOT files, "/path/to/repos/./CVSROOT" and + * "/path/to/repos/CVSROOT" are the arguments that are compared! + * + * This function ends up being called from the same places as + * strip_path, though what it does is much more conservative. Many + * comments about this operation (which was scattered around in + * several places in the source code) ran thus: + * + * ``repository ends with "/."; omit it. This sort of thing used + * to be taken care of by strip_path. Now we try to be more + * selective. I suspect that it would be even better to push it + * back further someday, so that the trailing "/." doesn't get into + * repository in the first place, but we haven't taken things that + * far yet.'' --Jim Kingdon (recurse.c, 07-Sep-97) + */ + +void +Sanitize_Repository_Name (char *repository) +{ + size_t len; + + assert (repository != NULL); + + strip_trailing_slashes (repository); + + len = strlen (repository); + if (len >= 2 + && repository[len - 1] == '.' + && ISSLASH (repository[len - 2])) + { + repository[len - 2] = '\0'; + } +} diff --git a/contrib/cvs-1.12.9/src/root.c b/contrib/cvs-1.12.9/src/root.c new file mode 100644 index 0000000000..a9475228cb --- /dev/null +++ b/contrib/cvs-1.12.9/src/root.c @@ -0,0 +1,896 @@ +/* + * Copyright (c) 1992, Mark D. Baushke + * Copyright (c) 2002, Derek R. Price + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * Name of Root + * + * Determine the path to the CVSROOT and set "Root" accordingly. + */ + +#include "cvs.h" +#include "getline.h" + +/* Printable names for things in the current_parsed_root->method enum variable. + Watch out if the enum is changed in cvs.h! */ + +const char method_names[][16] = { + "undefined", "local", "server (rsh)", "pserver", + "kserver", "gserver", "ext", "fork" +}; + +#ifndef DEBUG + +char * +Name_Root (char *dir, char *update_dir) +{ + FILE *fpin; + char *ret, *xupdate_dir; + char *root = NULL; + size_t root_allocated = 0; + char *tmp; + char *cvsadm; + char *cp; + int len; + + if (update_dir && *update_dir) + xupdate_dir = update_dir; + else + xupdate_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 + { + cvsadm = xstrdup (CVSADM); + tmp = xstrdup (CVSADM_ROOT); + } + + /* + * Do not bother looking for a readable file if there is no cvsadm + * directory present. + * + * 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. */ + if ((!isdir (cvsadm)) || (!isreadable (tmp))) + { + ret = NULL; + goto out; + } + + /* + * The assumption here is that the CVS Root is always contained in the + * first line of the "Root" file. + */ + fpin = open_file (tmp, "r"); + + if ((len = getline (&root, &root_allocated, fpin)) < 0) + { + int saved_errno = errno; + /* 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, saved_errno, "cannot read %s", CVSADM_ROOT); + error (0, 0, "please correct this problem"); + ret = NULL; + goto out; + } + fclose (fpin); + cp = root + (len - 1); + if (*cp == '\n') + *cp = '\0'; /* strip the newline */ + + /* + * root now contains a candidate for CVSroot. It must be an + * absolute pathname or specify a remote server. + */ + + if ( +#ifdef 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); + ret = NULL; + goto out; + } + +#ifdef CLIENT_SUPPORT + if ((strchr (root, ':') == NULL) && !isdir (root)) +#else /* ! CLIENT_SUPPORT */ + if (!isdir (root)) +#endif /* CLIENT_SUPPORT */ + { + error (0, 0, "in directory %s:", xupdate_dir); + error (0, 0, + "ignoring %s because it specifies a non-existent repository %s", + CVSADM_ROOT, root); + ret = NULL; + goto out; + } + + /* allocate space to return and fill it in */ + strip_trailing_slashes (root); + ret = xstrdup (root); + out: + free (cvsadm); + free (tmp); + if (root != NULL) + free (root); + return (ret); +} + + + +/* + * Write the CVS/Root file so that the environment variable CVSROOT + * and/or the -d option to cvs will be validated or not necessary for + * future work. + */ +void +Create_Root (const char *dir, const char *rootdir) +{ + FILE *fout; + char *tmp; + + if (noexec) + return; + + /* record the current cvs root */ + + if (rootdir != NULL) + { + if (dir != NULL) + { + tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); + (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); + } + else + 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 */ + + +/* The root_allow_* stuff maintains a list of valid CVSROOT + directories. Then we can check against them when a remote user + hands us a CVSROOT directory. */ + +static int root_allow_count; +static char **root_allow_vector; +static int root_allow_size; + +void +root_allow_add (char *arg) +{ + char *p; + + if (root_allow_size <= root_allow_count) + { + if (root_allow_size == 0) + { + root_allow_size = 1; + root_allow_vector = + (char **) xmalloc (root_allow_size * sizeof (char *)); + } + else + { + root_allow_size *= 2; + root_allow_vector = + (char **) xrealloc (root_allow_vector, + root_allow_size * sizeof (char *)); + } + + if (root_allow_vector == NULL) + { + no_memory: + /* 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"); + + exit (EXIT_FAILURE); + } + } + p = xmalloc (strlen (arg) + 1); + if (p == NULL) + goto no_memory; + strcpy (p, arg); + root_allow_vector[root_allow_count++] = p; +} + +void +root_allow_free (void) +{ + if (root_allow_vector != NULL) + free_names (&root_allow_count, root_allow_vector); + root_allow_size = 0; +} + +int +root_allow_ok (char *arg) +{ + int i; + + if (root_allow_count == 0) + { + /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10 + or later without reading the documentation about + --allow-root. Printing an error here doesn't disclose any + particularly useful information to an attacker because a + CVS server configured in this way won't let *anyone* in. */ + + /* Note that we are called from a context where we can spit + back "error" rather than waiting for the next request which + expects responses. */ + printf ("\ +error 0 Server configuration missing --allow-root in inetd.conf\n"); + exit (EXIT_FAILURE); + } + + for (i = 0; i < root_allow_count; ++i) + if (strcmp (root_allow_vector[i], arg) == 0) + return 1; + return 0; +} + + + +/* This global variable holds the global -d option. It is NULL if -d + was not used, which means that we must get the CVSroot information + from the CVSROOT environment variable or from a CVS/Root file. */ +char *CVSroot_cmdline; + + + +/* FIXME - Deglobalize this. */ +cvsroot_t *current_parsed_root = NULL; + + + +/* allocate and initialize a cvsroot_t + * + * We must initialize the strings to NULL so we know later what we should + * free + * + * Some of the other zeroes remain meaningful as, "never set, use default", + * or the like + */ +/* Functions which allocate memory are not pure. */ +static cvsroot_t *new_cvsroot_t(void) + __attribute__( (__malloc__) ); +static cvsroot_t * +new_cvsroot_t (void) +{ + cvsroot_t *newroot; + + /* gotta store it somewhere */ + newroot = xmalloc(sizeof(cvsroot_t)); + + newroot->original = NULL; + newroot->method = null_method; +#ifdef CLIENT_SUPPORT + newroot->username = NULL; + newroot->password = NULL; + newroot->hostname = NULL; + newroot->port = 0; + newroot->directory = NULL; + newroot->proxy_hostname = NULL; + newroot->proxy_port = 0; + newroot->isremote = 0; +#endif /* CLIENT_SUPPORT */ + + return newroot; +} + + + +/* Dispose of a cvsroot_t and its component parts */ +void +free_cvsroot_t (cvsroot_t *root) +{ + if (root->original != NULL) + free (root->original); + if (root->directory != NULL) + free (root->directory); +#ifdef CLIENT_SUPPORT + if (root->username != NULL) + free (root->username); + if (root->password != NULL) + { + /* I like to be paranoid */ + memset (root->password, 0, strlen (root->password)); + free (root->password); + } + if (root->hostname != NULL) + free (root->hostname); + if (root->proxy_hostname != NULL) + free (root->proxy_hostname); +#endif /* CLIENT_SUPPORT */ + free (root); +} + + + +/* + * Parse a CVSROOT string to allocate and return a new cvsroot_t structure. + * Valid specifications are: + * + * :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path + * [:(ext|server):][[user]@]host[:]/path + * [:local:[e:]]/path + * :fork:/path + * + * INPUTS + * root_in C String containing the CVSROOT to be parsed. + * + * RETURNS + * A pointer to a newly allocated cvsroot_t structure upon success and + * NULL upon failure. The caller is responsible for disposing of + * new structures with a call to free_cvsroot_t(). + * + * NOTES + * This would have been a lot easier to write in Perl. + * + * SEE ALSO + * free_cvsroot_t() + */ +cvsroot_t * +parse_cvsroot (const char *root_in) +{ + cvsroot_t *newroot; /* the new root to be returned */ + char *cvsroot_save; /* what we allocated so we can dispose + * it when finished */ + char *firstslash; /* save where the path spec starts + * while we parse + * [[user][:password]@]host[:[port]] + */ + char *cvsroot_copy, *p, *q; /* temporary pointers for parsing */ +#ifdef CLIENT_SUPPORT + int check_hostname, no_port, no_password, no_proxy; +#endif /* CLIENT_SUPPORT */ + + assert (root_in != NULL); + + TRACE (TRACE_FUNCTION, "parse_cvsroot ( %s )", root_in); + + /* allocate some space */ + newroot = new_cvsroot_t(); + + /* save the original string */ + newroot->original = xstrdup (root_in); + + /* and another copy we can munge while parsing */ + cvsroot_save = cvsroot_copy = xstrdup (root_in); + + if (*cvsroot_copy == ':') + { + char *method = ++cvsroot_copy; + + /* Access method specified, as in + * "cvs -d :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path", + * "cvs -d [:(ext|server):][[user]@]host[:]/path", + * "cvs -d :local:e:\path", + * "cvs -d :fork:/path". + * We need to get past that part of CVSroot before parsing the + * rest of it. + */ + + if (! (p = strchr (method, ':'))) + { + error (0, 0, "No closing `:' on method in CVSROOT."); + goto error_exit; + } + *p = '\0'; + cvsroot_copy = ++p; + +#ifdef CLIENT_SUPPORT + /* Look for method options, for instance, proxy, proxyport. + * Calling strtok again is saved until after parsing the method. + */ + method = strtok (method, ";"); +#endif /* CLIENT_SUPPORT */ + + /* Now we have an access method -- see if it's valid. */ + + if (strcmp (method, "local") == 0) + newroot->method = local_method; + else if (strcmp (method, "pserver") == 0) + newroot->method = pserver_method; + else if (strcmp (method, "kserver") == 0) + newroot->method = kserver_method; + else if (strcmp (method, "gserver") == 0) + newroot->method = gserver_method; + else if (strcmp (method, "server") == 0) + newroot->method = server_method; + else if (strcmp (method, "ext") == 0) + newroot->method = ext_method; + else if (strcmp (method, "fork") == 0) + newroot->method = fork_method; + else + { + error (0, 0, "Unknown method (`%s') in CVSROOT.", method); + goto error_exit; + } + +#ifdef CLIENT_SUPPORT + /* Parse the method options, for instance, proxy, proxyport */ + while (p = strtok (NULL, ";")) + { + char *q = strchr (p, '='); + if (q == NULL) + { + error (0, 0, "Option (`%s') has no argument in CVSROOT.", + p); + goto error_exit; + } + + *q++ = '\0'; + if (strcmp (p, "proxy") == 0) + { + newroot->proxy_hostname = xstrdup (q); + } + else if (strcmp (p, "proxyport") == 0) + { + char *r = q; + if (*r == '-') r++; + while (*r) + { + if (!isdigit(*r++)) + { + error (0, 0, +"CVSROOT may only specify a positive, non-zero, integer proxy port (not `%s').", + q); + goto error_exit; + } + } + if ((newroot->proxy_port = atoi (q)) <= 0) + error (0, 0, +"CVSROOT may only specify a positive, non-zero, integer proxy port (not `%s').", + q); + } + else + { + error (0, 0, "Unknown option (`%s') in CVSROOT.", p); + goto error_exit; + } + } +#endif /* CLIENT_SUPPORT */ + } + else + { + /* If the method isn't specified, assume EXT_METHOD if the string looks + like a relative path and LOCAL_METHOD otherwise. */ + + newroot->method = ((*cvsroot_copy != '/' && strchr (cvsroot_copy, '/')) + ? ext_method + : local_method); + } + + /* + * There are a few sanity checks we can do now, only knowing the + * method of this root. + */ +#ifndef DEBUG + /* Why do we avoid these checks when DEBUG is set? How is this used? */ +# ifndef CLIENT_SUPPORT + if (newroot->method != local_method) + { + error (0, 0, "CVSROOT is set for a remote access method but your"); + error (0, 0, "CVS executable doesn't support it."); + goto error_exit; + } +# endif +#endif /* ! DEBUG */ + +#ifdef CLIENT_SUPPORT + newroot->isremote = (newroot->method != local_method); + + if (readonlyfs && newroot->isremote) + error (1, 0, +"Read-only repository feature unavailable with remote roots (cvsroot = %s)", + cvsroot_copy); + + if ((newroot->method != local_method) +#ifdef CLIENT_SUPPORT + && (newroot->method != fork_method) +#endif /* SERVER_SUPPORT */ + ) + { + /* split the string into [[user][:password]@]host[:[port]] & /path + * + * this will allow some characters such as '@' & ':' to remain unquoted + * in the path portion of the spec + */ + if ((p = strchr (cvsroot_copy, '/')) == NULL) + { + error (0, 0, "CVSROOT requires a path spec:"); + error (0, 0, +":(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path"); + error (0, 0, "[:(ext|server):][[user]@]host[:]/path"); + goto error_exit; + } + firstslash = p; /* == NULL if '/' not in string */ + *p = '\0'; + + /* Check to see if there is a username[:password] in the string. */ + if ((p = strchr (cvsroot_copy, '@')) != NULL) + { + *p = '\0'; + /* check for a password */ + if ((q = strchr (cvsroot_copy, ':')) != NULL) + { + *q = '\0'; + newroot->password = xstrdup (++q); + /* Don't check for *newroot->password == '\0' since + * a user could conceivably wish to specify a blank password + * + * (newroot->password == NULL means to use the + * password from .cvspass) + */ + } + + /* copy the username */ + if (*cvsroot_copy != '\0') + /* a blank username is impossible, so leave it NULL in that + * case so we know to use the default username + */ + newroot->username = xstrdup (cvsroot_copy); + + cvsroot_copy = ++p; + } + + /* now deal with host[:[port]] */ + + /* the port */ + if ((p = strchr (cvsroot_copy, ':')) != NULL) + { + *p++ = '\0'; + if (strlen(p)) + { + q = p; + if (*q == '-') q++; + while (*q) + { + if (!isdigit(*q++)) + { + error (0, 0, +"CVSROOT may only specify a positive, non-zero, integer port (not `%s').", + p); + error (0, 0, + "Perhaps you entered a relative pathname?"); + goto error_exit; + } + } + if ((newroot->port = atoi (p)) <= 0) + { + error (0, 0, +"CVSROOT may only specify a positive, non-zero, integer port (not `%s').", + p); + error (0, 0, "Perhaps you entered a relative pathname?"); + goto error_exit; + } + } + } + + /* copy host */ + if (*cvsroot_copy != '\0') + /* blank hostnames are invalid, but for now leave the field NULL + * and catch the error during the sanity checks later + */ + newroot->hostname = xstrdup (cvsroot_copy); + + /* restore the '/' */ + cvsroot_copy = firstslash; + *cvsroot_copy = '/'; + } +#endif /* CLIENT_SUPPORT */ + + /* + * Parse the path for all methods. + */ + /* Here & local_cvsroot() should be the only places this needs to be + * called on a CVSROOT now. cvsroot->original is saved for error messages + * and, otherwise, we want no trailing slashes. + */ + Sanitize_Repository_Name (cvsroot_copy); + newroot->directory = xstrdup (cvsroot_copy); + + /* + * Do various sanity checks. + */ + +#ifdef CLIENT_SUPPORT + if (newroot->username && ! newroot->hostname) + { + error (0, 0, "Missing hostname in CVSROOT."); + goto error_exit; + } + + /* We won't have attempted to parse these without CLIENT_SUPPORT */ + check_hostname = 0; + no_password = 1; + no_proxy = 1; + no_port = 0; +#endif /* CLIENT_SUPPORT */ + switch (newroot->method) + { + case local_method: +#ifdef CLIENT_SUPPORT + if (newroot->username || newroot->hostname) + { + error (0, 0, "Can't specify hostname and username in CVSROOT"); + error (0, 0, "when using local access method."); + goto error_exit; + } +#endif /* CLIENT_SUPPORT */ + /* 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 (newroot->directory)) + { + error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", + newroot->directory); + error (0, 0, "when using local access method."); + goto error_exit; + } +#ifdef CLIENT_SUPPORT + /* We don't need to check for these in :local: mode, really, since + * we shouldn't be able to hit the code above which parses them, but + * I'm leaving them here in lieu of assertions. + */ + no_port = 1; + /* no_password already set */ +#endif /* CLIENT_SUPPORT */ + break; +#ifdef CLIENT_SUPPORT + case fork_method: + /* We want :fork: to behave the same as other remote access + methods. Therefore, don't check to see that the repository + name is absolute -- let the server do it. */ + if (newroot->username || newroot->hostname) + { + error (0, 0, "Can't specify hostname and username in CVSROOT"); + error (0, 0, "when using fork access method."); + goto error_exit; + } + newroot->hostname = xstrdup("server"); /* for error messages */ + if (!isabsolute (newroot->directory)) + { + error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", + newroot->directory); + error (0, 0, "when using fork access method."); + goto error_exit; + } + no_port = 1; + /* no_password already set */ + break; + case kserver_method: +# ifndef HAVE_KERBEROS + error (0, 0, "CVSROOT is set for a kerberos access method but your"); + error (0, 0, "CVS executable doesn't support it."); + goto error_exit; +# else + check_hostname = 1; + /* no_password already set */ + break; +# endif + case gserver_method: +# ifndef HAVE_GSSAPI + error (0, 0, "CVSROOT is set for a GSSAPI access method but your"); + error (0, 0, "CVS executable doesn't support it."); + goto error_exit; +# else + check_hostname = 1; + no_proxy = 0; + /* no_password already set */ + break; +# endif + case server_method: + case ext_method: + no_port = 1; + /* no_password already set */ + check_hostname = 1; + break; + case pserver_method: + no_password = 0; + no_proxy = 0; + check_hostname = 1; + break; +#endif /* CLIENT_SUPPORT */ + default: + error (1, 0, "Invalid method found in parse_cvsroot"); + } + +#ifdef CLIENT_SUPPORT + if (no_password && newroot->password) + { + error (0, 0, "CVSROOT password specification is only valid for"); + error (0, 0, "pserver connection method."); + goto error_exit; + } + if (no_proxy && (newroot->proxy_hostname || newroot->proxy_port)) + { + error (0, 0, +"CVSROOT proxy specification is only valid for gserver and"); + error (0, 0, "pserver connection methods."); + goto error_exit; + } + + if (!newroot->proxy_hostname && newroot->proxy_port) + { + error (0, 0, "Proxy port specified in CVSROOT without proxy host."); + goto error_exit; + } + + if (check_hostname && !newroot->hostname) + { + error (0, 0, "Didn't specify hostname in CVSROOT."); + goto error_exit; + } + + if (no_port && newroot->port) + { + error (0, 0, +"CVSROOT port specification is only valid for gserver, kserver,"); + error (0, 0, "and pserver connection methods."); + goto error_exit; + } +#endif /*CLIENT_SUPPORT */ + + if (*newroot->directory == '\0') + { + error (0, 0, "Missing directory in CVSROOT."); + goto error_exit; + } + + /* Hooray! We finally parsed it! */ + free (cvsroot_save); + return newroot; + +error_exit: + free (cvsroot_save); + free_cvsroot_t (newroot); + return NULL; +} + + + +#ifdef AUTH_CLIENT_SUPPORT +/* Use root->username, root->hostname, root->port, and root->directory + * to create a normalized CVSROOT fit for the .cvspass file + * + * username defaults to the result of getcaller() + * port defaults to the result of get_cvs_port_number() + * + * FIXME - we could cache the canonicalized version of a root inside the + * cvsroot_t, but we'd have to un'const the input here and stop expecting the + * caller to be responsible for our return value + */ +char * +normalize_cvsroot (const cvsroot_t *root) +{ + char *cvsroot_canonical; + char *p, *hostname; + size_t length; + + /* use a lower case hostname since we know hostnames are case insensitive */ + /* Some logic says we should be tacking our domain name on too if it isn't + * there already, but for now this works. Reverse->Forward lookups are + * almost certainly too much since that would make CVS immune to some of + * the DNS trickery that makes life easier for sysadmins when they want to + * move a repository or the like + */ + p = hostname = xstrdup (root->hostname); + while (*p) + { + *p = tolower (*p); + p++; + } + + cvsroot_canonical = asnprintf (NULL, &length, ":pserver:%s@%s:%d%s", + root->username ? root->username + : getcaller(), + hostname, get_cvs_port_number (root), + root->directory); + + free (hostname); + return cvsroot_canonical; +} +#endif /* AUTH_CLIENT_SUPPORT */ + + + +/* allocate and return a cvsroot_t structure set up as if we're using the local + * repository DIR. */ +cvsroot_t * +local_cvsroot (const char *dir) +{ + cvsroot_t *newroot = new_cvsroot_t(); + + newroot->original = xstrdup(dir); + newroot->method = local_method; + newroot->directory = xstrdup(dir); + /* Here and parse_cvsroot() should be the only places this needs to be + * called on a CVSROOT now. cvsroot->original is saved for error messages + * and, otherwise, we want no trailing slashes. + */ + Sanitize_Repository_Name( newroot->directory ); + return newroot; +} + + + +#ifdef DEBUG +/* This is for testing the parsing function. Use + + gcc -I. -I.. -I../lib -DDEBUG root.c -o root + + to compile. */ + +#include + +char *program_name = "testing"; +char *cvs_cmd_name = "parse_cvsroot"; /* XXX is this used??? */ + +/* Toy versions of various functions when debugging under unix. Yes, + these make various bad assumptions, but they're pretty easy to + debug when something goes wrong. */ + +int +isabsolute( const char *dir ) +{ + return (dir && (*dir == '/')); +} + +void +main( int argc, char *argv[] ) +{ + program_name = argv[0]; + + if (argc != 2) + { + fprintf (stderr, "Usage: %s \n", program_name); + exit (2); + } + + if ((current_parsed_root = parse_cvsroot (argv[1])) == NULL) + { + fprintf (stderr, "%s: Parsing failed.\n", program_name); + exit (1); + } + printf ("CVSroot: %s\n", argv[1]); + printf ("current_parsed_root->method: %s\n", method_names[current_parsed_root->method]); + printf ("current_parsed_root->username: %s\n", + current_parsed_root->username ? current_parsed_root->username : "NULL"); + printf ("current_parsed_root->hostname: %s\n", + current_parsed_root->hostname ? current_parsed_root->hostname : "NULL"); + printf ("current_parsed_root->directory: %s\n", current_parsed_root->directory); + + exit (0); + /* NOTREACHED */ +} +#endif diff --git a/contrib/cvs-1.12.9/src/root.h b/contrib/cvs-1.12.9/src/root.h new file mode 100644 index 0000000000..b812eef101 --- /dev/null +++ b/contrib/cvs-1.12.9/src/root.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2001, Derek Price and others + * 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. + */ + +/* CVSroot data structures */ + +/* Access method specified in CVSroot. */ +typedef enum { + null_method, + local_method, + server_method, + pserver_method, + kserver_method, + gserver_method, + ext_method, + fork_method +} CVSmethod; +extern const char method_names[][16]; /* change this in root.c if you change + the enum above */ + +typedef struct cvsroot_s { + char *original; /* The complete source CVSroot string. */ + CVSmethod method; /* One of the enum values above. */ + char *directory; /* The directory name. */ +#ifdef CLIENT_SUPPORT + char *username; /* The username or NULL if method == local. */ + char *password; /* The password or NULL if method == local. */ + char *hostname; /* The hostname or NULL if method == local. */ + int port; /* The port or zero if method == local. */ + char *proxy_hostname; /* The hostname of the proxy server, or NULL + * when method == local or no proxy will be + * used. + */ + int proxy_port; /* The port of the proxy or zero, as above. */ + unsigned char isremote; /* Nonzero if we are doing remote access. */ +#endif /* CLIENT_SUPPORT */ +} cvsroot_t; diff --git a/contrib/cvs-1.12.9/src/rsh-client.c b/contrib/cvs-1.12.9/src/rsh-client.c new file mode 100644 index 0000000000..a41d6d3e26 --- /dev/null +++ b/contrib/cvs-1.12.9/src/rsh-client.c @@ -0,0 +1,194 @@ +/* CVS rsh client stuff. + + 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 + +#include "cvs.h" +#include "buffer.h" + +#ifdef CLIENT_SUPPORT + +#include "rsh-client.h" + +#ifndef NO_EXT_METHOD + +/* Contact the server by starting it with rsh. */ + +/* Right now, we have two different definitions for this function, + depending on whether we start the rsh server using popenRW or not. + This isn't ideal, and the best thing would probably be to change + the OS/2 port to be more like the regular Unix client (i.e., by + implementing piped_child)... but I'm doing something else at the + moment, and wish to make only one change at a time. -Karl */ + +# ifdef START_RSH_WITH_POPEN_RW + +/* This is actually a crock -- it's OS/2-specific, for no one else + uses it. If I get time, I want to make piped_child and all the + other stuff in os2/run.c work right. In the meantime, this gets us + up and running, and that's most important. */ + +void +start_rsh_server( cvsroot_t *root, struct buffer **to_server_p, + struct buffer **from_server_p ) +{ + int pipes[2]; + int child_pid; + + /* 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_DFLT; + 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"; +# endif /* RSH_NEEDS_BINARY_FLAG */ + + /* Then we strcat more things on the end one by one. */ + if (root->username != NULL) + { + rsh_argv[i++] = "-l"; + rsh_argv[i++] = root->username; + } + + rsh_argv[i++] = root->hostname; + rsh_argv[i++] = cvs_server; + rsh_argv[i++] = "server"; + + /* Mark the end of the arg list. */ + rsh_argv[i] = (char *) NULL; + + if (trace) + { + fprintf (stderr, " -> Starting server: "); + for (i = 0; rsh_argv[i]; i++) + fprintf (stderr, "%s ", rsh_argv[i]); + putc ('\n', stderr); + } + + /* Do the deed. */ + child_pid = popenRW (rsh_argv, pipes); + if (child_pid < 0) + error (1, errno, "cannot start server via rsh"); + + /* Give caller the file descriptors in a form it can deal with. */ + make_bufs_from_fds (pipes[0], pipes[1], child_pid, to_server_p, from_server_p, 0); +} + +# else /* ! START_RSH_WITH_POPEN_RW */ + +void +start_rsh_server (cvsroot_t *root, struct buffer **to_server_p, struct buffer **from_server_p) +{ + /* 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; + int tofd, fromfd; + int child_pid; + + if (!cvs_rsh) + cvs_rsh = RSH_DFLT; + if (!cvs_server) + cvs_server = "cvs"; + + /* Pass the command to rsh as a single string. This shouldn't + affect most rsh servers at all, and will pacify some buggy + 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) + 8); + + /* 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). */ + sprintf (command, "%s server", cvs_server); + + { + char *argv[10]; + char **p = argv; + + *p++ = cvs_rsh; + + /* If the login names differ between client and server + * pass it on to rsh. + */ + if (root->username != NULL) + { + *p++ = "-l"; + *p++ = root->username; + } + + *p++ = root->hostname; + *p++ = command; + *p++ = NULL; + + if (trace) + { + int i; + + fprintf (stderr, " -> Starting server: "); + for (i = 0; argv[i]; i++) + fprintf (stderr, "%s ", argv[i]); + putc ('\n', stderr); + } + child_pid = piped_child (argv, &tofd, &fromfd); + + if (child_pid < 0) + error (1, errno, "cannot start server via rsh"); + } + free (command); + + make_bufs_from_fds (tofd, fromfd, child_pid, to_server_p, from_server_p, 0); +} + +# endif /* START_RSH_WITH_POPEN_RW */ + +#endif /* NO_EXT_METHOD */ + +#endif /* CLIENT_SUPPORT */ diff --git a/contrib/cvs-1.12.9/src/rsh-client.h b/contrib/cvs-1.12.9/src/rsh-client.h new file mode 100644 index 0000000000..01d102b557 --- /dev/null +++ b/contrib/cvs-1.12.9/src/rsh-client.h @@ -0,0 +1,19 @@ +/* CVS rsh client stuff. + + 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. */ + + +#ifndef RSH_CLIENT_H__ +#define RSH_CLIENT_H__ + +void start_rsh_server (cvsroot_t *, struct buffer **, struct buffer **); + +#endif diff --git a/contrib/cvs-1.12.9/src/run.c b/contrib/cvs-1.12.9/src/run.c new file mode 100644 index 0000000000..6a768cf4c0 --- /dev/null +++ b/contrib/cvs-1.12.9/src/run.c @@ -0,0 +1,499 @@ +/* run.c --- routines for executing subprocesses. + + 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. */ + +#include "cvs.h" + +#ifndef HAVE_UNISTD_H +extern int execvp (char *file, char **argv); +#endif + +static void run_add_arg (const char *s); + + + +/* + * To exec a program under CVS, first call run_setup() to setup initial + * arguments. The argument to run_setup will be parsed into whitespace + * separated words and added to the global run_argv list. + * + * Then, optionally call run_arg() for each additional argument that you'd like + * to pass to the executed program. + * + * Finally, call run_exec() to execute the program with the specified arguments. + * 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_argv; +static int run_argc; +static int run_argc_allocated; + +/* VARARGS */ +void +run_setup( const char *prog ) +{ + int i; + char *run_prog; + char *buf, *d, *s; + size_t length; + size_t doff; + char inquotes; + int dolastarg; + + /* clean out any malloc'ed values from run_argv */ + for (i = 0; i < run_argc; i++) + { + if (run_argv[i]) + { + free (run_argv[i]); + run_argv[i] = (char *) 0; + } + } + run_argc = 0; + + run_prog = xstrdup (prog); + + s = run_prog; + d = buf = NULL; + length = 0; + dolastarg = 1; + inquotes = '\0'; + doff = d - buf; + expand_string(&buf, &length, doff + 1); + d = buf + doff; + while ((*d = *s++) != '\0') + { + switch (*d) + { + case '\\': + if (*s) *d = *s++; + d++; + break; + case '"': + case '\'': + if (inquotes == *d) inquotes = '\0'; + else inquotes = *d; + break; + case ' ': + case '\t': + if (inquotes) d++; + else + { + *d = '\0'; + run_add_arg (buf); + d = buf; + while (isspace(*s)) s++; + if (!*s) dolastarg = 0; + } + break; + default: + d++; + break; + } + doff = d - buf; + expand_string(&buf, &length, doff + 1); + d = buf + doff; + } + if (dolastarg) run_add_arg (buf); + /* put each word into run_argv, allocating it as we go */ + if (buf) free (buf); + free (run_prog); +} + +void +run_arg (const char *s) +{ + run_add_arg (s); +} + +static void +run_add_arg (const char *s) +{ + /* allocate more argv entries if we've run out */ + if (run_argc >= run_argc_allocated) + { + run_argc_allocated += 50; + run_argv = (char **) xrealloc ((char *) run_argv, + run_argc_allocated * sizeof (char **)); + } + + if (s) + run_argv[run_argc++] = xstrdup (s); + else + run_argv[run_argc] = (char *) 0; /* not post-incremented on purpose! */ +} + + + +int +run_exec (const char *stin, const char *stout, const char *sterr, int flags) +{ + int shin, shout, sherr; + int mode_out, mode_err; + int status; + int rc = -1; + int rerrno = 0; + int pid, w; + +#ifdef POSIX_SIGNALS + sigset_t sigset_mask, sigset_omask; + struct sigaction act, iact, qact; + +#else +#ifdef BSD_SIGNALS + int mask; + struct sigvec vec, ivec, qvec; + +#else + RETSIGTYPE (*istat) (), (*qstat) (); +#endif +#endif + + if (trace) + { +#ifdef SERVER_SUPPORT + cvs_outerr (server_active ? "S" : " ", 1); +#endif + cvs_outerr ("-> system(", 0); + run_print (stderr); + cvs_outerr (")\n", 0); + } + if (noexec && (flags & RUN_REALLY) == 0) + return 0; + + /* make sure that we are null terminated, since we didn't calloc */ + run_add_arg ((char *)0); + + /* setup default file descriptor numbers */ + shin = 0; + shout = 1; + sherr = 2; + + /* set the file modes for stdout and stderr */ + mode_out = mode_err = O_WRONLY | O_CREAT; + mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC); + mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC); + + if (stin && (shin = open (stin, O_RDONLY)) == -1) + { + rerrno = errno; + error (0, errno, "cannot open %s for reading (prog %s)", + stin, run_argv[0]); + goto out0; + } + if (stout && (shout = open (stout, mode_out, 0666)) == -1) + { + rerrno = errno; + error (0, errno, "cannot open %s for writing (prog %s)", + stout, run_argv[0]); + goto out1; + } + if (sterr && (flags & RUN_COMBINED) == 0) + { + if ((sherr = open (sterr, mode_err, 0666)) == -1) + { + rerrno = errno; + error (0, errno, "cannot open %s for writing (prog %s)", + sterr, run_argv[0]); + goto out2; + } + } + + /* Make sure we don't flush this twice, once in the subprocess. */ + cvs_flushout(); + cvs_flusherr(); + + /* The output files, if any, are now created. Do the fork and dups. + + We use vfork not so much for a performance boost (the + performance boost, if any, is modest on most modern unices), + but for the sake of systems without a memory management unit, + which find it difficult or impossible to implement fork at all + (e.g. Amiga). The other solution is spawn (see + windows-NT/run.c). */ + +#ifdef HAVE_VFORK + pid = vfork (); +#else + pid = fork (); +#endif + if (pid == 0) + { + if (shin != 0) + { + (void) dup2 (shin, 0); + (void) close (shin); + } + if (shout != 1) + { + (void) dup2 (shout, 1); + (void) close (shout); + } + if (flags & RUN_COMBINED) + (void) dup2 (1, 2); + else if (sherr != 2) + { + (void) dup2 (sherr, 2); + (void) close (sherr); + } + +#ifdef SETXID_SUPPORT + /* + ** This prevents a user from creating a privileged shell + ** from the text editor when the SETXID_SUPPORT option is selected. + */ + if (!strcmp (run_argv[0], Editor) && setegid (getgid ())) + { + error (0, errno, "cannot set egid to gid"); + _exit (127); + } +#endif + + /* dup'ing is done. try to run it now */ + (void) execvp (run_argv[0], run_argv); + error (0, errno, "cannot exec %s", run_argv[0]); + _exit (127); + } + else if (pid == -1) + { + rerrno = errno; + goto out; + } + + /* the parent. Ignore some signals for now */ +#ifdef POSIX_SIGNALS + if (flags & RUN_SIGIGNORE) + { + act.sa_handler = SIG_IGN; + (void) sigemptyset (&act.sa_mask); + act.sa_flags = 0; + (void) sigaction (SIGINT, &act, &iact); + (void) sigaction (SIGQUIT, &act, &qact); + } + else + { + (void) sigemptyset (&sigset_mask); + (void) sigaddset (&sigset_mask, SIGINT); + (void) sigaddset (&sigset_mask, SIGQUIT); + (void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask); + } +#else +#ifdef BSD_SIGNALS + if (flags & RUN_SIGIGNORE) + { + memset ((char *)&vec, 0, sizeof (vec)); + vec.sv_handler = SIG_IGN; + (void) sigvec (SIGINT, &vec, &ivec); + (void) sigvec (SIGQUIT, &vec, &qvec); + } + else + mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT)); +#else + istat = signal (SIGINT, SIG_IGN); + qstat = signal (SIGQUIT, SIG_IGN); +#endif +#endif + + /* wait for our process to die and munge return status */ +#ifdef POSIX_SIGNALS + while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR) + ; +#else + while ((w = wait (&status)) != pid) + { + if (w == -1 && errno != EINTR) + break; + } +#endif + + if (w == -1) + { + rc = -1; + rerrno = errno; + } +#ifndef VMS /* status is return status */ + else if (WIFEXITED (status)) + rc = WEXITSTATUS (status); + else if (WIFSIGNALED (status)) + { + if (WTERMSIG (status) == SIGPIPE) + error (1, 0, "broken pipe"); + rc = 2; + } + else + rc = 1; +#else /* VMS */ + rc = WEXITSTATUS (status); +#endif /* VMS */ + + /* restore the signals */ +#ifdef POSIX_SIGNALS + if (flags & RUN_SIGIGNORE) + { + (void) sigaction (SIGINT, &iact, (struct sigaction *)NULL); + (void) sigaction (SIGQUIT, &qact, (struct sigaction *)NULL); + } + else + (void) sigprocmask (SIG_SETMASK, &sigset_omask, (sigset_t *)NULL); +#else +#ifdef BSD_SIGNALS + if (flags & RUN_SIGIGNORE) + { + (void) sigvec (SIGINT, &ivec, (struct sigvec *)NULL); + (void) sigvec (SIGQUIT, &qvec, (struct sigvec *)NULL); + } + else + (void) sigsetmask (mask); +#else + (void) signal (SIGINT, istat); + (void) signal (SIGQUIT, qstat); +#endif +#endif + + /* cleanup the open file descriptors */ + out: + if (sterr) + (void) close (sherr); + else + /* ensure things are received by the parent in the correct order + * relative to the protocol pipe + */ + cvs_flusherr(); + out2: + if (stout) + (void) close (shout); + else + /* ensure things are received by the parent in the correct order + * relative to the protocol pipe + */ + cvs_flushout(); + out1: + if (stin) + (void) close (shin); + + out0: + if (rerrno) + errno = rerrno; + return rc; +} + + + +void +run_print (FILE *fp) +{ + int i; + void (*outfn) (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"); + /* Solely to placate gcc -Wall. + FIXME: it'd be better to use a function named `fatal' that + is known never to return. Then kludges wouldn't be necessary. */ + outfn = NULL; + } + + for (i = 0; i < run_argc; i++) + { + (*outfn) ("'", 1); + (*outfn) (run_argv[i], 0); + (*outfn) ("'", 1); + if (i != run_argc - 1) + (*outfn) (" ", 1); + } +} + + + +/* Return value is NULL for error, or if noexec was set. If there was an + error, return NULL and I'm not sure whether errno was set (the Red Hat + Linux 4.1 popen manpage was kind of vague but discouraging; and the noexec + case complicates this even aside from popen behavior). */ + +FILE * +run_popen (const char *cmd, const char *mode) +{ + TRACE ( 1, "run_popen(%s,%s)", cmd, mode ); + if (noexec) + return (NULL); + + return (popen (cmd, mode)); +} + + + +int +piped_child (char *const *command, int *tofdp, int *fromfdp) +{ + int pid; + int to_child_pipe[2]; + int from_child_pipe[2]; + + if (pipe (to_child_pipe) < 0) + error (1, errno, "cannot create pipe"); + if (pipe (from_child_pipe) < 0) + error (1, errno, "cannot create pipe"); + +#ifdef USE_SETMODE_BINARY + setmode (to_child_pipe[0], O_BINARY); + setmode (to_child_pipe[1], O_BINARY); + setmode (from_child_pipe[0], O_BINARY); + setmode (from_child_pipe[1], O_BINARY); +#endif + +#ifdef HAVE_VFORK + pid = vfork (); +#else + pid = fork (); +#endif + if (pid < 0) + error (1, errno, "cannot fork"); + if (pid == 0) + { + if (dup2 (to_child_pipe[0], STDIN_FILENO) < 0) + error (1, errno, "cannot dup2 pipe"); + if (close (to_child_pipe[1]) < 0) + error (1, errno, "cannot close pipe"); + if (close (from_child_pipe[0]) < 0) + error (1, errno, "cannot close pipe"); + if (dup2 (from_child_pipe[1], STDOUT_FILENO) < 0) + error (1, errno, "cannot dup2 pipe"); + + /* Okay to cast out const below - execvp don't return anyhow. */ + execvp ((char *)command[0], (char **)command); + error (1, errno, "cannot exec %s", command[0]); + } + if (close (to_child_pipe[0]) < 0) + error (1, errno, "cannot close pipe"); + if (close (from_child_pipe[1]) < 0) + error (1, errno, "cannot close pipe"); + + *tofdp = to_child_pipe[1]; + *fromfdp = from_child_pipe[0]; + return pid; +} + + +void +close_on_exec (int fd) +{ +#ifdef F_SETFD + if (fcntl (fd, F_SETFD, 1) == -1) + error (1, errno, "can't set close-on-exec flag on %d", fd); +#endif +} diff --git a/contrib/cvs-1.12.9/src/scramble.c b/contrib/cvs-1.12.9/src/scramble.c new file mode 100644 index 0000000000..126d140f8b --- /dev/null +++ b/contrib/cvs-1.12.9/src/scramble.c @@ -0,0 +1,243 @@ +/* + * Trivially encode strings to protect them from innocent eyes (i.e., + * inadvertent password compromises, like a network administrator + * who's watching packets for legitimate reasons and accidentally sees + * the password protocol go by). + * + * This is NOT secure encryption. + * + * It would be tempting to encode the password according to username + * and repository, so that the same password would encode to a + * different string when used with different usernames and/or + * repositories. However, then users would not be able to cut and + * paste passwords around. They're not supposed to anyway, but we all + * know they will, and there's no reason to make it harder for them if + * we're not trying to provide real security anyway. + */ + +/* Set this to test as a standalone program. */ +/* #define DIAGNOSTIC */ + +#ifndef DIAGNOSTIC +#include "cvs.h" +#else /* ! DIAGNOSTIC */ +/* cvs.h won't define this for us */ +#define AUTH_CLIENT_SUPPORT +#define xmalloc malloc +/* Use "gcc -fwritable-strings". */ +#include +#include +#include +#endif /* ! DIAGNOSTIC */ + +#if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT) + +/* Map characters to each other randomly and symmetrically, A <--> B. + * + * We divide the ASCII character set into 3 domains: control chars (0 + * thru 31), printing chars (32 through 126), and "meta"-chars (127 + * through 255). The control chars map _to_ themselves, the printing + * chars map _among_ themselves, and the meta chars map _among_ + * themselves. Why is this thus? + * + * No character in any of these domains maps to a character in another + * domain, because I'm not sure what characters are valid in + * passwords, or what tools people are likely to use to cut and paste + * them. It seems prudent not to introduce control or meta chars, + * unless the user introduced them first. And having the control + * chars all map to themselves insures that newline and + * carriage-return are safely handled. + */ + +static unsigned char +shifts[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87, + 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105, + 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35, + 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56, + 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48, + 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223, + 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190, + 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193, + 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212, + 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246, + 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176, + 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127, + 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195, + 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 }; + + +/* SCRAMBLE and DESCRAMBLE work like this: + * + * scramble(STR) returns SCRM, a scrambled copy of STR. SCRM[0] is a + * single letter indicating the scrambling method. As of this + * writing, the only valid method is 'A', but check the code for more + * up-to-date information. The copy will have been allocated with + * xmalloc(). + * + * descramble(SCRM) returns STR, again in its own xmalloc'd space. + * descramble() uses SCRM[0] to determine which method of unscrambling + * to use. If it does not recognize the method, it dies with error. + */ + +/* Return a xmalloc'd, scrambled version of STR. */ +char * +scramble (char *str) +{ + int i; + char *s; + + /* +2 to hold the 'A' prefix that indicates which version of + scrambling this is (the first, obviously, since we only do one + kind of scrambling so far), and then the '\0' of course. */ + s = (char *) xmalloc (strlen (str) + 2); + + /* Scramble (TM) version prefix. */ + s[0] = 'A'; + strcpy (s + 1, str); + + for (i = 1; s[i]; i++) + s[i] = shifts[(unsigned char)(s[i])]; + + return s; +} + +/* Decode the string in place. */ +char * +descramble (char *str) +{ + char *s; + int i; + + /* For now we can only handle one kind of scrambling. In the future + there may be other kinds, and this `if' will become a `switch'. */ + if (str[0] != 'A') +#ifndef DIAGNOSTIC + error (1, 0, "descramble: unknown scrambling method"); +#else /* DIAGNOSTIC */ + { + fprintf (stderr, "descramble: unknown scrambling method\n", str); + fflush (stderr); + exit (EXIT_FAILURE); + } +#endif /* DIAGNOSTIC */ + + /* Method `A' is symmetrical, so scramble again to decrypt. */ + s = scramble (str + 1); + + /* Shift the whole string one char to the left, pushing the unwanted + 'A' off the left end. Safe, because s is null-terminated. */ + for (i = 0; s[i]; i++) + s[i] = s[i + 1]; + + return s; +} + +#endif /* (AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT) from top of file */ + +#ifdef DIAGNOSTIC +int +main( int argc, char **argv ) +{ + int i; + char *e, *m, biggie[256]; + + char *cleartexts[5]; + cleartexts[0] = "first"; + cleartexts[1] = "the second"; + cleartexts[2] = "this is the third"; + cleartexts[3] = "$#% !!\\3"; + cleartexts[4] = biggie; + + /* Set up the most important test string: */ + /* Can't have a real ASCII zero in the string, because we want to + use printf, so we substitute the character zero. */ + biggie[0] = '0'; + /* The rest of the string gets straight ascending ASCII. */ + for (i = 1; i < 256; i++) + biggie[i] = i; + + /* Test all the strings. */ + for (i = 0; i < 5; i++) + { + printf ("clear%d: %s\n", i, cleartexts[i]); + e = scramble (cleartexts[i]); + printf ("scram%d: %s\n", i, e); + m = descramble (e); + free (e); + printf ("clear%d: %s\n\n", i, m); + free (m); + } + + fflush (stdout); + return 0; +} +#endif /* DIAGNOSTIC */ + +/* + * ;;; The Emacs Lisp that did the dirty work ;;; + * (progn + * + * ;; Helper func. + * (defun random-elt (lst) + * (let* ((len (length lst)) + * (rnd (random len))) + * (nth rnd lst))) + * + * ;; A list of all characters under 127, each appearing once. + * (setq non-meta-chars + * (let ((i 0) + * (l nil)) + * (while (< i 127) + * (setq l (cons i l) + * i (1+ i))) + * l)) + * + * ;; A list of all characters 127 and above, each appearing once. + * (setq meta-chars + * (let ((i 127) + * (l nil)) + * (while (< i 256) + * (setq l (cons i l) + * i (1+ i))) + * l)) + * + * ;; A vector that will hold the chars in a random order. + * (setq scrambled-chars (make-vector 256 0)) + * + * ;; These characters should map to themselves. + * (let ((i 0)) + * (while (< i 32) + * (aset scrambled-chars i i) + * (setq non-meta-chars (delete i non-meta-chars) + * i (1+ i)))) + * + * ;; Assign random (but unique) values, within the non-meta chars. + * (let ((i 32)) + * (while (< i 127) + * (let ((ch (random-elt non-meta-chars))) + * (if (= 0 (aref scrambled-chars i)) + * (progn + * (aset scrambled-chars i ch) + * (aset scrambled-chars ch i) + * (setq non-meta-chars (delete ch non-meta-chars) + * non-meta-chars (delete i non-meta-chars)))) + * (setq i (1+ i))))) + * + * ;; Assign random (but unique) values, within the non-meta chars. + * (let ((i 127)) + * (while (< i 256) + * (let ((ch (random-elt meta-chars))) + * (if (= 0 (aref scrambled-chars i)) + * (progn + * (aset scrambled-chars i ch) + * (aset scrambled-chars ch i) + * (setq meta-chars (delete ch meta-chars) + * meta-chars (delete i meta-chars)))) + * (setq i (1+ i))))) + * + * ;; Now use the `scrambled-chars' vector to get your C array. + * ) + */ diff --git a/contrib/cvs-1.12.9/src/server.c b/contrib/cvs-1.12.9/src/server.c new file mode 100644 index 0000000000..1d309cf51e --- /dev/null +++ b/contrib/cvs-1.12.9/src/server.c @@ -0,0 +1,6747 @@ +/* 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 "watch.h" +#include "edit.h" +#include "fileattr.h" +#include "getline.h" +#include "getnline.h" +#include "buffer.h" + +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) + +# ifdef HAVE_GSSAPI +# include "gssapi-client.h" + +/* This stuff isn't included solely with SERVER_SUPPORT since some of these + * functions (encryption & the like) get compiled with or without server + * support. + * + * FIXME - They should be in a different file. + */ +# include +/* We use Kerberos 5 routines to map the GSSAPI credential to a user + name. */ +# include + +static void gserver_authenticate_connection (void); + +/* Whether we are already wrapping GSSAPI communication. */ +static int cvs_gssapi_wrapping; + +# endif /* HAVE_GSSAPI */ +#endif /* defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) */ + +#ifdef SERVER_SUPPORT + +#ifdef HAVE_WINSOCK_H +#include +#endif + +#if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI) +#include +#endif + +#ifdef HAVE_SYSLOG_H +# include +# ifndef LOG_DAEMON /* for ancient syslogs */ +# define LOG_DAEMON 0 +# endif +#endif + +#ifdef HAVE_KERBEROS +# include +# include +# 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 "xselect.h" + +#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 + +/* For initgroups(). */ +#if HAVE_INITGROUPS +#include +#endif /* HAVE_INITGROUPS */ + +# ifdef AUTH_SERVER_SUPPORT + +# ifdef HAVE_GETSPNAM +# include +# endif + +/* 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. */ +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. */ +static char *Pserver_Repos = NULL; + +/* Should we check for system usernames/passwords? Can be changed by + CVSROOT/config. */ +int system_auth = 1; + +# 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 + * which expects a single directory within it, starting with a slash. + */ +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 void server_write_entries (void); + +/* 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. */ + +struct fd_buffer +{ + /* The file descriptor. */ + int fd; + /* Nonzero if the file descriptor is in blocking mode. */ + int blocking; +}; + +static struct buffer *fd_buffer_initialize + (int, int, void (*) (struct buffer *)); +static int fd_buffer_input (void *, char *, int, int, int *); +static int fd_buffer_output (void *, const char *, int, int *); +static int fd_buffer_flush (void *); +static int fd_buffer_block (void *, int); +static int fd_buffer_shutdown (struct buffer *); + +/* 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 (int fd, int input, void (*memory) (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, + fd_buffer_shutdown, + memory, + n); +} + +/* The buffer input function for a buffer built on a file descriptor. */ + +static int +fd_buffer_input (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 + { + /* 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 (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) + { + 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; + } + + return 0; +} + +/* The buffer flush function for a buffer built on a file descriptor. */ + +/*ARGSUSED*/ +static int +fd_buffer_flush (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 (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; +} + +/* The buffer shutdown function for a buffer built on a file descriptor. */ + +static int +fd_buffer_shutdown (struct buffer *buf) +{ + free (buf->closure); + buf->closure = NULL; + return 0; +} + +/* Populate all of the directories between BASE_DIR and its relative + subdirectory DIR with CVSADM directories. Return 0 for success or + errno value. */ +static int create_adm_p (char *, char *); + +static int +create_adm_p (char *base_dir, char *dir) +{ + char *dir_where_cvsadm_lives, *dir_to_register, *p, *tmp; + int retval, done; + FILE *f; + + if (strcmp (dir, ".") == 0) + return 0; /* nothing to do */ + + /* Allocate some space for our directory-munging string. */ + p = xmalloc (strlen (dir) + 1); + if (p == NULL) + return ENOMEM; + + dir_where_cvsadm_lives = xmalloc (strlen (base_dir) + strlen (dir) + 100); + if (dir_where_cvsadm_lives == NULL) + return ENOMEM; + + /* Allocate some space for the temporary string in which we will + construct filenames. */ + tmp = xmalloc (strlen (base_dir) + strlen (dir) + 100); + if (tmp == NULL) + return ENOMEM; + + + /* We make several passes through this loop. On the first pass, + we simply create the CVSADM directory in the deepest directory. + For each subsequent pass, we try to remove the last path + element from DIR, create the CVSADM directory in the remaining + pathname, and register the subdirectory in the newly created + CVSADM directory. */ + + retval = done = 0; + + strcpy (p, dir); + strcpy (dir_where_cvsadm_lives, base_dir); + strcat (dir_where_cvsadm_lives, "/"); + strcat (dir_where_cvsadm_lives, p); + dir_to_register = NULL; + + while (1) + { + /* Create CVSADM. */ + (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM); + if ((CVS_MKDIR (tmp, 0777) < 0) && (errno != EEXIST)) + { + retval = errno; + goto finish; + } + + /* Create CVSADM_REP. */ + (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM_REP); + if (! isfile (tmp)) + { + /* Use Emptydir as the placeholder until the client sends + us the real value. This code is similar to checkout.c + (emptydir_name), but the code below returns errors + differently. */ + + char *empty; + empty = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + sizeof (CVSNULLREPOS) + + 3); + if (! empty) + { + retval = ENOMEM; + goto finish; + } + + /* Create the directory name. */ + (void) sprintf (empty, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSNULLREPOS); + + /* Create the directory if it doesn't exist. */ + if (! isfile (empty)) + { + mode_t omask; + omask = umask (cvsumask); + if (CVS_MKDIR (empty, 0777) < 0) + { + retval = errno; + free (empty); + goto finish; + } + (void) umask (omask); + } + + f = CVS_FOPEN (tmp, "w"); + if (f == NULL) + { + retval = errno; + free (empty); + goto finish; + } + /* Write the directory name to CVSADM_REP. */ + if (fprintf (f, "%s\n", empty) < 0) + { + retval = errno; + fclose (f); + free (empty); + goto finish; + } + if (fclose (f) == EOF) + { + retval = errno; + free (empty); + goto finish; + } + + /* Clean up after ourselves. */ + free (empty); + } + + /* Create CVSADM_ENT. We open in append mode because we + don't want to clobber an existing Entries file. */ + (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM_ENT); + f = CVS_FOPEN (tmp, "a"); + if (f == NULL) + { + retval = errno; + goto finish; + } + if (fclose (f) == EOF) + { + retval = errno; + goto finish; + } + + if (dir_to_register != NULL) + { + /* FIXME: Yes, this results in duplicate entries in the + Entries.Log file, but it doesn't currently matter. We + might need to change this later on to make sure that we + only write one entry. */ + + Subdir_Register ((List *) NULL, dir_where_cvsadm_lives, + dir_to_register); + } + + if (done) + break; + + dir_to_register = strrchr (p, '/'); + if (dir_to_register == NULL) + { + dir_to_register = p; + strcpy (dir_where_cvsadm_lives, base_dir); + done = 1; + } + else + { + *dir_to_register = '\0'; + dir_to_register++; + strcpy (dir_where_cvsadm_lives, base_dir); + strcat (dir_where_cvsadm_lives, "/"); + strcat (dir_where_cvsadm_lives, p); + } + } + + finish: + free (tmp); + free (dir_where_cvsadm_lives); + free (p); + return retval; +} + +/* + * Make directory DIR, including all intermediate directories if necessary. + * Returns 0 for success or errno code. + */ +static int mkdir_p (char *); + +static int +mkdir_p (char *dir) +{ + char *p; + char *q = xmalloc (strlen (dir) + 1); + int retval; + + if (q == NULL) + return ENOMEM; + + retval = 0; + + /* + * Skip over leading slash if present. We won't bother to try to + * make '/'. + */ + p = dir + 1; + while (1) + { + while (*p != '/' && *p != '\0') + ++p; + if (*p == '/') + { + strncpy (q, dir, p - dir); + q[p - dir] = '\0'; + if (q[p - dir - 1] != '/' && CVS_MKDIR (q, 0777) < 0) + { + int saved_errno = errno; + + if (saved_errno != EEXIST + && ((saved_errno != EACCES && saved_errno != EROFS) + || !isdir (q))) + { + retval = saved_errno; + goto done; + } + } + ++p; + } + else + { + if (CVS_MKDIR (dir, 0777) < 0) + retval = errno; + goto done; + } + } + done: + free (q); + return retval; +} + +/* + * 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 (int status) +{ + char *msg; + char tmpstr[80]; + + buf_output0 (buf_to_net, "error "); + msg = strerror (status); + if (msg == NULL) + { + sprintf (tmpstr, "unknown error %d", status); + msg = tmpstr; + } + buf_output0 (buf_to_net, msg); + buf_append_char (buf_to_net, '\n'); + + buf_flush (buf_to_net, 0); +} + +static int pending_error; +/* + * Malloc'd text for pending error. Each line must start with "E ". The + * last line should not end with a newline. + */ +static char *pending_error_text; + +/* 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 (void) +{ + if (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 + buf_output0 (buf_to_net, "error \n"); + + buf_flush (buf_to_net, 0); + + pending_error = 0; + free (pending_error_text); + pending_error_text = NULL; + return 1; + } + else if (pending_error) + { + print_error (pending_error); + pending_error = 0; + return 1; + } + else + return 0; +} + +/* Is an error pending? */ +#define error_pending() (pending_error || pending_error_text) + +static int alloc_pending (size_t size); + +/* Allocate SIZE bytes for pending_error_text and return nonzero + if we could do it. */ +static int +alloc_pending (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 = xmalloc (size); + if (pending_error_text == NULL) + { + pending_error = ENOMEM; + return 0; + } + return 1; +} + +static void serve_is_modified (char *); + +static int supported_response (char *); + +static int +supported_response (char *name) +{ + struct response *rs; + + for (rs = responses; rs->name != NULL; ++rs) + if (strcmp (rs->name, name) == 0) + return rs->status == rs_supported; + error (1, 0, "internal error: testing support for unknown response?"); + /* NOTREACHED */ + return 0; +} + +static void +serve_valid_responses (char *arg) +{ + char *p = arg; + char *q; + struct response *rs; + do + { + q = strchr (p, ' '); + if (q != NULL) + *q++ = '\0'; + for (rs = responses; rs->name != NULL; ++rs) + { + if (strcmp (rs->name, p) == 0) + break; + } + if (rs->name == NULL) + /* + * It is a response we have never heard of (and thus never + * will want to use). So don't worry about it. + */ + ; + else + rs->status = rs_supported; + p = q; + } while (q != NULL); + for (rs = responses; rs->name != NULL; ++rs) + { + if (rs->status == rs_essential) + { + 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); + + exit (EXIT_FAILURE); + } + else if (rs->status == rs_optional) + rs->status = rs_not_supported; + } +} + +static void +serve_root (char *arg) +{ + char *env; + char *path; + + if (error_pending()) return; + + if (!isabsolute (arg)) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E Root %s must be an absolute pathname", arg); + return; + } + + /* Sending "Root" twice is invalid. + + The other way to handle a duplicate Root requests would be as a + request to clear out all state and start over as if it was a + new connection. Doing this would cause interoperability + headaches, so it should be a different request, if there is + any reason why such a feature is needed. */ + if (current_parsed_root != NULL) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E Protocol error: Duplicate Root request, for %s", arg); + return; + } + +#ifdef AUTH_SERVER_SUPPORT + if (Pserver_Repos != NULL) + { + if (strcmp (Pserver_Repos, arg) != 0) + { + if (alloc_pending (80 + strlen (Pserver_Repos) + strlen (arg))) + /* The explicitness is to aid people who are writing clients. + I don't see how this information could help an + attacker. */ + sprintf (pending_error_text, "\ +E Protocol error: Root says \"%s\" but pserver says \"%s\"", + arg, Pserver_Repos); + return; + } + } +#endif + + current_parsed_root = local_cvsroot (arg); + + /* For pserver, this will already have happened, and the call will do + nothing. But for rsh, we need to do it now. */ + parse_config (current_parsed_root->directory); + + path = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + 2); + if (path == NULL) + { + pending_error = ENOMEM; + return; + } + (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM); + if (!isaccessible (path, R_OK | X_OK)) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (path))) + sprintf (pending_error_text, "E Cannot access %s", path); + pending_error = save_errno; + } + free (path); + +#ifdef HAVE_PUTENV + env = xmalloc (strlen (CVSROOT_ENV) + strlen (current_parsed_root->directory) + 2); + if (env == NULL) + { + pending_error = ENOMEM; + return; + } + (void) sprintf (env, "%s=%s", CVSROOT_ENV, current_parsed_root->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 (char *path) +{ + TRACE (TRACE_FUNCTION, "server_pathname_check (%s)", + path ? path : "(null)"); + + /* 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)) + /* Giving an error is actually kind of a cop-out, in the sense + that it would be nice for "cvs co -d /foo/bar/baz" to work. + A quick fix in the server would be requiring Max-dotdot of + at least one if pathnames are absolute, and then putting + /abs/foo/bar/baz in the temp dir beside the /d/d/d stuff. + A cleaner fix in the server might be to decouple the + pathnames we pass back to the client from pathnames in our + temp directory (this would also probably remove the need + for Max-dotdot). A fix in the client would have the client + turn it into "cd /foo/bar; cvs co -d baz" (more or less). + This probably has some problems with pathnames which appear + in messages. */ + error ( 1, 0, + "absolute pathnames invalid for server (specified `%s')", + 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); + } +} + +static int outside_root (char *); + +/* Is file or directory REPOS an absolute pathname within the + current_parsed_root->directory? If yes, return 0. If no, set pending_error + and return 1. */ +static int +outside_root (char *repos) +{ + size_t repos_len = strlen (repos); + size_t root_len = strlen (current_parsed_root->directory); + + /* isabsolute (repos) should always be true, but + this is a good security precaution regardless. -DRP + */ + if (!isabsolute (repos)) + { + if (alloc_pending (repos_len + 80)) + sprintf (pending_error_text, "\ +E protocol error: %s is not absolute", repos); + return 1; + } + + if (repos_len < root_len + || strncmp (current_parsed_root->directory, repos, root_len) != 0) + { + not_within: + if (alloc_pending (strlen (current_parsed_root->directory) + + strlen (repos) + + 80)) + sprintf (pending_error_text, "\ +E protocol error: directory '%s' not within root '%s'", + repos, current_parsed_root->directory); + return 1; + } + if (repos_len > root_len) + { + if (repos[root_len] != '/') + goto not_within; + if (pathname_levels (repos + root_len + 1) > 0) + goto not_within; + } + return 0; +} + +static int outside_dir (char *); + +/* Is file or directory FILE outside the current directory (that is, does + it contain '/')? If no, return 0. If yes, set pending_error + and return 1. */ +static int +outside_dir (char *file) +{ + if (strchr (file, '/') != NULL) + { + if (alloc_pending (strlen (file) + + 80)) + sprintf (pending_error_text, "\ +E protocol error: directory '%s' not within current directory", + file); + return 1; + } + return 0; +} + +/* + * Add as many directories to the temp directory as the client tells us it + * will use "..", so we never try to access something outside the temp + * directory via "..". + */ +static void +serve_max_dotdot (char *arg) +{ + int lim = atoi (arg); + int i; + char *p; + + if (lim < 0 || lim > 10000) + return; + p = xmalloc (strlen (server_temp_dir) + 2 * lim + 10); + if (p == NULL) + { + pending_error = ENOMEM; + return; + } + strcpy (p, server_temp_dir); + for (i = 0; i < lim; ++i) + strcat (p, "/d"); + if (server_temp_dir != orig_server_temp_dir) + free (server_temp_dir); + server_temp_dir = p; + max_dotdot_limit = lim; +} + +static char *dir_name; + +static void +dirswitch (char *dir, char *repos) +{ + int status; + FILE *f; + size_t dir_len; + + TRACE (TRACE_FUNCTION, "dirswitch (%s, %s)", dir ? dir : "(null)", + repos ? repos : "(null)"); + + server_write_entries (); + + if (error_pending()) return; + + /* Check for bad directory name. + + FIXME: could/should unify these checks with server_pathname_check + except they need to report errors differently. */ + if (isabsolute (dir)) + { + if (alloc_pending (80 + strlen (dir))) + sprintf ( pending_error_text, + "E absolute pathnames invalid for server (specified `%s')", + dir); + return; + } + if (pathname_levels (dir) > max_dotdot_limit) + { + if (alloc_pending (80 + strlen (dir))) + sprintf (pending_error_text, + "E protocol error: `%s' has too many ..", dir); + return; + } + + dir_len = strlen (dir); + + /* Check for a trailing '/'. This is not ISSLASH 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 (dir_len > 0 + && dir[dir_len - 1] == '/') + { + if (alloc_pending (80 + dir_len)) + sprintf (pending_error_text, + "E protocol error: invalid directory syntax in %s", dir); + return; + } + + if (dir_name != NULL) + free (dir_name); + + dir_name = xmalloc (strlen (server_temp_dir) + dir_len + 40); + if (dir_name == NULL) + { + pending_error = ENOMEM; + return; + } + + strcpy (dir_name, server_temp_dir); + strcat (dir_name, "/"); + strcat (dir_name, dir); + + status = mkdir_p (dir_name); + if (status != 0 + && status != EEXIST) + { + if (alloc_pending (80 + strlen (dir_name))) + sprintf (pending_error_text, "E cannot mkdir %s", dir_name); + pending_error = status; + return; + } + + /* We need to create adm directories in all path elements because + we want the server to descend them, even if the client hasn't + sent the appropriate "Argument xxx" command to match the + already-sent "Directory xxx" command. See recurse.c + (start_recursion) for a big discussion of this. */ + + status = create_adm_p (server_temp_dir, dir); + if (status != 0) + { + if (alloc_pending (80 + strlen (dir_name))) + sprintf (pending_error_text, "E cannot create_adm_p %s", dir_name); + pending_error = status; + return; + } + + if ( CVS_CHDIR (dir_name) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (dir_name))) + sprintf (pending_error_text, "E cannot change to %s", dir_name); + pending_error = save_errno; + return; + } + /* + * This is pretty much like calling Create_Admin, but Create_Admin doesn't + * report errors in the right way for us. + */ + if ((CVS_MKDIR (CVSADM, 0777) < 0) && (errno != EEXIST)) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (dir_name) + strlen (CVSADM))) + sprintf (pending_error_text, + "E cannot mkdir %s/%s", dir_name, CVSADM); + pending_error = save_errno; + return; + } + + /* The following will overwrite the contents of CVSADM_REP. This + is the correct behavior -- mkdir_p may have written a + placeholder value to this file and we need to insert the + correct value. */ + + f = CVS_FOPEN (CVSADM_REP, "w"); + if (f == NULL) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (dir_name) + strlen (CVSADM_REP))) + sprintf (pending_error_text, + "E cannot open %s/%s", dir_name, CVSADM_REP); + pending_error = save_errno; + return; + } + if (fprintf (f, "%s", repos) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (dir_name) + strlen (CVSADM_REP))) + sprintf (pending_error_text, + "E error writing %s/%s", dir_name, CVSADM_REP); + pending_error = save_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 + && current_parsed_root != NULL + && current_parsed_root->directory != NULL + && strcmp (current_parsed_root->directory, repos) == 0) + { + if (fprintf (f, "/.") < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (dir_name) + strlen (CVSADM_REP))) + sprintf (pending_error_text, + "E error writing %s/%s", dir_name, CVSADM_REP); + pending_error = save_errno; + fclose (f); + return; + } + } + if (fprintf (f, "\n") < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (dir_name) + strlen (CVSADM_REP))) + sprintf (pending_error_text, + "E error writing %s/%s", dir_name, CVSADM_REP); + pending_error = save_errno; + fclose (f); + return; + } + if (fclose (f) == EOF) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (dir_name) + strlen (CVSADM_REP))) + sprintf (pending_error_text, + "E error closing %s/%s", dir_name, CVSADM_REP); + pending_error = save_errno; + return; + } + /* 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) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_ENT))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_ENT); + pending_error = save_errno; + return; + } + if (fclose (f) == EOF) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_ENT))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_ENT); + pending_error = save_errno; + return; + } +} + + + +static void +serve_repository (char *arg) +{ + if (alloc_pending (80)) + strcpy (pending_error_text, + "E Repository request is obsolete; aborted"); + return; +} + + + +static void +serve_directory (char *arg) +{ + int status; + char *repos; + + TRACE (TRACE_FUNCTION, "serve_directory (%s)", arg ? arg : "(null)"); + + status = buf_read_line (buf_from_net, &repos, NULL); + if (status == 0) + { + if (!outside_root (repos)) + dirswitch (arg, repos); + free (repos); + } + else if (status == -2) + { + pending_error = ENOMEM; + } + else + { + pending_error_text = xmalloc (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; + } + } +} + + + +static void +serve_static_directory (char *arg) +{ + FILE *f; + + if (error_pending ()) return; + + f = CVS_FOPEN (CVSADM_ENTSTAT, "w+"); + if (f == NULL) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_ENTSTAT))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_ENTSTAT); + pending_error = save_errno; + return; + } + if (fclose (f) == EOF) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_ENTSTAT))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_ENTSTAT); + pending_error = save_errno; + return; + } +} + +static void +serve_sticky (char *arg) +{ + FILE *f; + + if (error_pending ()) return; + + f = CVS_FOPEN (CVSADM_TAG, "w+"); + if (f == NULL) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_TAG))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_TAG); + pending_error = save_errno; + return; + } + if (fprintf (f, "%s\n", arg) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_TAG))) + sprintf (pending_error_text, "E cannot write to %s", CVSADM_TAG); + pending_error = save_errno; + return; + } + if (fclose (f) == EOF) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_TAG))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_TAG); + pending_error = save_errno; + return; + } +} + +/* + * 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 + * spiffy in-process gzip support working, perhaps the compressed + * pieces could be sent over as they're ready, if the network is fast + * enough. Or something. + */ +static void +receive_partial_file (int size, int file) +{ + while (size > 0) + { + int status, nread; + char *data; + + status = buf_read_data (buf_from_net, size, &data, &nread); + if (status != 0) + { + if (status == -2) + pending_error = ENOMEM; + else + { + pending_error_text = xmalloc (80); + 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 + { + sprintf (pending_error_text, + "E error reading from client"); + pending_error = status; + } + } + return; + } + + size -= nread; + + while (nread > 0) + { + int nwrote; + + nwrote = write (file, data, nread); + if (nwrote < 0) + { + int save_errno = errno; + if (alloc_pending (40)) + strcpy (pending_error_text, "E unable to write"); + pending_error = save_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; + data += nwrote; + } + } +} + +/* Receive SIZE bytes, write to filename FILE. */ +static void +receive_file (int size, char *file, int gzipped) +{ + int fd; + char *arg = file; + + /* Write the file. */ + fd = CVS_OPEN (arg, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) + { + int save_errno = errno; + if (alloc_pending (40 + strlen (arg))) + sprintf (pending_error_text, "E cannot open %s", arg); + pending_error = save_errno; + return; + } + + if (gzipped) + { + /* Using gunzip_and_write isn't really a high-performance + approach, because it keeps the whole thing in memory + (contiguous memory, worse yet). But it seems easier to + code than the alternative (and less vulnerable to subtle + bugs). Given that this feature is mainly for + compatibility, that is the better tradeoff. */ + + int toread = size; + char *filebuf; + char *p; + + filebuf = xmalloc (size); + p = filebuf; + /* If NULL, we still want to read the data and discard it. */ + + while (toread > 0) + { + int status, nread; + char *data; + + status = buf_read_data (buf_from_net, toread, &data, &nread); + if (status != 0) + { + if (status == -2) + pending_error = ENOMEM; + else + { + pending_error_text = xmalloc (80); + 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 + { + sprintf (pending_error_text, + "E error reading from client"); + pending_error = status; + } + } + return; + } + + toread -= nread; + + if (filebuf != NULL) + { + memcpy (p, data, nread); + p += nread; + } + } + if (filebuf == NULL) + { + pending_error = ENOMEM; + goto out; + } + + if (gunzip_and_write (fd, file, (unsigned char *) filebuf, size)) + { + if (alloc_pending (80)) + sprintf (pending_error_text, + "E aborting due to compression error"); + } + free (filebuf); + } + else + receive_partial_file (size, fd); + + if (pending_error_text) + { + char *p = xrealloc (pending_error_text, + strlen (pending_error_text) + strlen (arg) + 30); + if (p) + { + pending_error_text = p; + sprintf (p + strlen (p), ", file %s", arg); + } + /* else original string is supposed to be unchanged */ + } + + out: + if (close (fd) < 0 && !error_pending ()) + { + int save_errno = errno; + if (alloc_pending (40 + strlen (arg))) + sprintf (pending_error_text, "E cannot close %s", arg); + pending_error = save_errno; + return; + } +} + + + +/* Kopt for the next file sent in Modified or Is-modified. */ +static char *kopt; + +/* Timestamp (Checkin-time) for next file sent in Modified or + Is-modified. */ +static int checkin_time_valid; +static time_t checkin_time; + + + +static void +serve_modified (char *arg) +{ + int size, status; + char *size_text; + char *mode_text; + + int gzipped = 0; + + /* + * 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. + */ + + status = buf_read_line (buf_from_net, &mode_text, NULL); + if (status != 0) + { + if (status == -2) + pending_error = ENOMEM; + else + { + pending_error_text = xmalloc (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; + } + } + } + return; + } + + status = buf_read_line (buf_from_net, &size_text, NULL); + if (status != 0) + { + if (status == -2) + pending_error = ENOMEM; + else + { + pending_error_text = xmalloc (80 + strlen (arg)); + if (pending_error_text == NULL) + pending_error = ENOMEM; + else + { + 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 = status; + } + } + } + free (mode_text); + return; + } + if (size_text[0] == 'z') + { + gzipped = 1; + size = atoi (size_text + 1); + } + else + 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; + } + free (mode_text); + return; + } + + if (outside_dir (arg)) + { + free (mode_text); + return; + } + + if (size >= 0) + { + receive_file (size, arg, gzipped); + if (error_pending ()) + { + free (mode_text); + return; + } + } + + if (checkin_time_valid) + { + struct utimbuf t; + + memset (&t, 0, sizeof (t)); + t.modtime = t.actime = checkin_time; + if (utime (arg, &t) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, "E cannot utime %s", arg); + pending_error = save_errno; + free (mode_text); + return; + } + checkin_time_valid = 0; + } + + { + int status = change_mode (arg, mode_text, 0); + free (mode_text); + if (status) + { + if (alloc_pending (40 + strlen (arg))) + sprintf (pending_error_text, + "E cannot change mode for %s", arg); + pending_error = status; + return; + } + } + + /* Make sure that the Entries indicate the right kopt. We probably + could do this even in the non-kopt case and, I think, save a stat() + call in time_stamp_server. But for conservatism I'm leaving the + non-kopt case alone. */ + if (kopt != NULL) + serve_is_modified (arg); +} + + + +static void +serve_enable_unchanged (char *arg) +{ +} + + + +struct an_entry { + struct an_entry *next; + char *entry; +}; + +static struct an_entry *entries; + + + +static void +serve_unchanged (char *arg) +{ + struct an_entry *p; + char *name; + char *cp; + char *timefield; + + if (error_pending ()) return; + + if (outside_dir (arg)) + return; + + /* Rewrite entries file to have `=' in timestamp field. */ + 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) + { + if (!(timefield = strchr (cp + 1, '/')) || *++timefield == '\0') + { + /* We didn't find the record separator or it is followed by + * the end of the string, so just exit. + */ + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Malformed Entry encountered."); + return; + } + /* If the time field is not currently empty, then one of + * serve_modified, serve_is_modified, & serve_unchanged were + * already called for this file. We would like to ignore the + * reinvocation silently or, better yet, exit with an error + * message, but we just avoid the copy-forward and overwrite the + * value from the last invocation instead. See the comment below + * for more. + */ + if (*timefield == '/') + { + /* Copy forward one character. Space was allocated for this + * already in serve_entry(). */ + cp = timefield + strlen (timefield); + cp[1] = '\0'; + while (cp > timefield) + { + *cp = cp[-1]; + --cp; + } + } + else if (timefield[1] != '/') + { + /* Obliterate anything else in TIMEFIELD. This is again to + * support the broken CVSNT clients mentioned below, in + * conjunction with strict timestamp string boundry checking in + * time_stamp_server() from vers_ts.c & file_has_conflict() + * from subr.c, since the broken clients used to send malformed + * timestamp fields in the Entry request that they then + * depended on the subsequent Unchanged request to overwrite. + */ + char *d = timefield + 1; + if (cp = strchr (d, '/')) + { + while (*cp) + { + *d++ = *cp++; + } + *d = '\0'; + } + } + /* If *TIMEFIELD wasn't '/', we assume that it was because of + * multiple calls to Is-modified & Unchanged by the client and + * just overwrite the value from the last call. Technically, we + * should probably either ignore calls after the first or send the + * client an error, since the client/server protocol specification + * specifies that only one call to either Is-Modified or Unchanged + * is allowed, but broken versions of CVSNT (at least 2.0.34 - + * 2.0.41, reported fixed in 2.0.41a) and the WinCVS & TortoiseCVS + * clients which depend on those broken versions of CVSNT (WinCVS + * 1.3 & at least one TortoiseCVS release) rely on this behavior. + */ + *timefield = '='; + break; + } + } +} + + + +static void +serve_is_modified (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 (outside_dir (arg)) + return; + + /* 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) + { + if (!(timefield = strchr (cp + 1, '/')) || *++timefield == '\0') + { + /* We didn't find the record separator or it is followed by + * the end of the string, so just exit. + */ + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Malformed Entry encountered."); + return; + } + /* If the time field is not currently empty, then one of + * serve_modified, serve_is_modified, & serve_unchanged were + * already called for this file. We would like to ignore the + * reinvocation silently or, better yet, exit with an error + * message, but we just avoid the copy-forward and overwrite the + * value from the last invocation instead. See the comment below + * for more. + */ + if (*timefield == '/') + { + /* Copy forward one character. Space was allocated for this + * already in serve_entry(). */ + cp = timefield + strlen (timefield); + cp[1] = '\0'; + while (cp > timefield) + { + *cp = cp[-1]; + --cp; + } + } + /* If *TIMEFIELD wasn't '/', we assume that it was because of + * multiple calls to Is-modified & Unchanged by the client and + * just overwrite the value from the last call. Technically, we + * should probably either ignore calls after the first or send the + * client an error, since the client/server protocol specification + * specifies that only one call to either Is-Modified or Unchanged + * is allowed, but broken versions of CVSNT (at least 2.0.34 - + * 2.0.41, reported fixed in 2.0.41a) and the WinCVS & TortoiseCVS + * clients which depend on those broken versions of CVSNT (WinCVS + * 1.3 & at least one TortoiseCVS release) rely on this behavior. + */ + *timefield++ = 'M'; + if (kopt != NULL) + { + if (alloc_pending (strlen (name) + 80)) + sprintf (pending_error_text, + "E protocol error: both Kopt and Entry for %s", + arg); + free (kopt); + kopt = NULL; + return; + } + 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. */ + p = xmalloc (sizeof (struct an_entry)); + if (p == NULL) + { + pending_error = ENOMEM; + return; + } + p->entry = xmalloc (strlen (arg) + 80); + if (p->entry == NULL) + { + pending_error = ENOMEM; + free (p); + return; + } + strcpy (p->entry, "/"); + strcat (p->entry, arg); + strcat (p->entry, "//D/"); + if (kopt != NULL) + { + strcat (p->entry, kopt); + free (kopt); + kopt = NULL; + } + strcat (p->entry, "/"); + p->next = entries; + entries = p; + } +} + + + +static void +serve_entry (char *arg) +{ + struct an_entry *p; + char *cp; + int i = 0; + if (error_pending()) return; + + /* Verify that the entry is well-formed. This can avoid problems later. + * At the moment we only check that the Entry contains five slashes in + * approximately the correct locations since some of the code makes + * assumptions about this. + */ + cp = arg; + if (*cp == 'D') cp++; + while (i++ < 5) + { + if (!cp || *cp != '/') + { + if (alloc_pending (80)) + sprintf (pending_error_text, + "E protocol error: Malformed Entry"); + return; + } + cp = strchr (cp + 1, '/'); + } + + p = xmalloc (sizeof (struct an_entry)); + if (p == NULL) + { + pending_error = ENOMEM; + return; + } + /* Leave space for serve_unchanged to write '=' if it wants. */ + cp = xmalloc (strlen (arg) + 2); + if (cp == NULL) + { + pending_error = ENOMEM; + return; + } + strcpy (cp, arg); + p->next = entries; + p->entry = cp; + entries = p; +} + + + +static void +serve_kopt (char *arg) +{ + if (error_pending ()) + return; + + if (kopt != NULL) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E protocol error: duplicate Kopt request: %s", arg); + return; + } + + /* Do some sanity checks. In particular, that it is not too long. + This lets the rest of the code not worry so much about buffer + overrun attacks. Probably should call RCS_check_kflag here, + but that would mean changing RCS_check_kflag to handle errors + other than via exit(), fprintf(), and such. */ + if (strlen (arg) > 10) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E protocol error: invalid Kopt request: %s", arg); + return; + } + + kopt = xmalloc (strlen (arg) + 1); + if (kopt == NULL) + { + pending_error = ENOMEM; + return; + } + strcpy (kopt, arg); +} + +static void serve_checkin_time (char *); + +static void +serve_checkin_time (char *arg) +{ + if (error_pending ()) + return; + + if (checkin_time_valid) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E protocol error: duplicate Checkin-time request: %s", + arg); + return; + } + + checkin_time = get_date (arg, NULL); + if (checkin_time == (time_t)-1) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, "E cannot parse date %s", arg); + return; + } + checkin_time_valid = 1; +} + +static void +server_write_entries (void) +{ + FILE *f; + struct an_entry *p; + struct an_entry *q; + + if (entries == NULL) + return; + + f = NULL; + /* Note that we free all the entries regardless of errors. */ + if (!error_pending ()) + { + /* 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) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_ENT))) + sprintf (pending_error_text, "E cannot open %s", CVSADM_ENT); + pending_error = save_errno; + } + } + for (p = entries; p != NULL;) + { + if (!error_pending ()) + { + if (fprintf (f, "%s\n", p->entry) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen(CVSADM_ENT))) + sprintf (pending_error_text, + "E cannot write to %s", CVSADM_ENT); + pending_error = save_errno; + } + } + free (p->entry); + q = p->next; + free (p); + p = q; + } + entries = NULL; + if (f != NULL && fclose (f) == EOF && !error_pending ()) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (CVSADM_ENT))) + sprintf (pending_error_text, "E cannot close %s", CVSADM_ENT); + pending_error = save_errno; + } +} + +struct notify_note { + /* Directory in which this notification happens. xmalloc'd*/ + char *dir; + + /* xmalloc'd. */ + char *filename; + + /* The following three all in one xmalloc'd block, pointed to by TYPE. + Each '\0' terminated. */ + /* "E" or "U". */ + char *type; + /* time+host+dir */ + char *val; + char *watches; + + struct notify_note *next; +}; + +static struct notify_note *notify_list; +/* Used while building list, to point to the last node that already exists. */ +static struct notify_note *last_node; + +static void serve_notify (char *); + +static void +serve_notify (char *arg) +{ + struct notify_note *new = NULL; + char *data = NULL; + int status; + + if (error_pending ()) return; + + if (outside_dir (arg)) + return; + + if (dir_name == NULL) + goto error; + + new = (struct notify_note *) xmalloc (sizeof (struct notify_note)); + if (new == NULL) + { + pending_error = ENOMEM; + return; + } + new->dir = xmalloc (strlen (dir_name) + 1); + new->filename = xmalloc (strlen (arg) + 1); + if (new->dir == NULL || new->filename == NULL) + { + pending_error = ENOMEM; + if (new->dir != NULL) + free (new->dir); + free (new); + return; + } + strcpy (new->dir, dir_name); + strcpy (new->filename, arg); + + status = buf_read_line (buf_from_net, &data, (int *) NULL); + if (status != 0) + { + if (status == -2) + pending_error = ENOMEM; + else + { + pending_error_text = xmalloc (80 + strlen (arg)); + if (pending_error_text == NULL) + pending_error = ENOMEM; + else + { + 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; + } + } + } + free (new->filename); + free (new->dir); + free (new); + } + else + { + char *cp; + + if (!data[0]) + goto error; + + if (strchr (data, '+')) + goto error; + + new->type = data; + if (data[1] != '\t') + goto error; + data[1] = '\0'; + cp = data + 2; + new->val = cp; + cp = strchr (cp, '\t'); + if (cp == NULL) + goto error; + *cp++ = '+'; + cp = strchr (cp, '\t'); + if (cp == NULL) + goto error; + *cp++ = '+'; + cp = strchr (cp, '\t'); + if (cp == NULL) + goto error; + *cp++ = '\0'; + new->watches = cp; + /* If there is another tab, ignore everything after it, + for future expansion. */ + cp = strchr (cp, '\t'); + if (cp != NULL) + { + *cp = '\0'; + } + + new->next = NULL; + + if (last_node == NULL) + { + notify_list = new; + } + else + last_node->next = new; + last_node = new; + } + return; + error: + pending_error = 0; + if (alloc_pending (80)) + strcpy (pending_error_text, + "E Protocol error; misformed Notify request"); + if (data != NULL) + free (data); + if (new != NULL) + { + free (new->filename); + free (new->dir); + free (new); + } + return; +} + +/* Process all the Notify requests that we have stored up. Returns 0 + if successful, if not prints error message (via error()) and + returns negative value. */ +static int +server_notify (void) +{ + struct notify_note *p; + char *repos; + + TRACE (TRACE_FUNCTION, "server_notify()"); + + while (notify_list != NULL) + { + if ( CVS_CHDIR (notify_list->dir) < 0) + { + error (0, errno, "cannot change to %s", notify_list->dir); + return -1; + } + repos = Name_Repository (NULL, NULL); + + lock_dir_for_write (repos); + + fileattr_startdir (repos); + + notify_do (*notify_list->type, notify_list->filename, getcaller(), + notify_list->val, notify_list->watches, repos); + + buf_output0 (buf_to_net, "Notified "); + { + char *dir = notify_list->dir + strlen (server_temp_dir) + 1; + if (dir[0] == '\0') + buf_append_char (buf_to_net, '.'); + else + buf_output0 (buf_to_net, dir); + buf_append_char (buf_to_net, '/'); + buf_append_char (buf_to_net, '\n'); + } + 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'); + free (repos); + + p = notify_list->next; + free (notify_list->filename); + free (notify_list->dir); + free (notify_list->type); + free (notify_list); + notify_list = p; + + fileattr_write (); + fileattr_free (); + + Lock_Cleanup (); + } + + last_node = NULL; + + /* 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; +} + +static int argument_count; +static char **argument_vector; +static int argument_vector_size; + +static void +serve_argument (char *arg) +{ + char *p; + + if (error_pending()) return; + + if (argument_count >= 10000) + { + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Protocol error: too many arguments"); + return; + } + + if (argument_vector_size <= argument_count) + { + argument_vector_size *= 2; + argument_vector = + (char **) xrealloc ((char *)argument_vector, + argument_vector_size * sizeof (char *)); + if (argument_vector == NULL) + { + pending_error = ENOMEM; + return; + } + } + p = xmalloc (strlen (arg) + 1); + if (p == NULL) + { + pending_error = ENOMEM; + return; + } + strcpy (p, arg); + argument_vector[argument_count++] = p; +} + +static void +serve_argumentx (char *arg) +{ + char *p; + + if (error_pending()) return; + + if (argument_count <= 1) + { + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Protocol error: called argumentx without prior call to argument"); + return; + } + + p = argument_vector[argument_count - 1]; + p = xrealloc (p, strlen (p) + 1 + strlen (arg) + 1); + if (p == NULL) + { + pending_error = ENOMEM; + return; + } + strcat (p, "\n"); + strcat (p, arg); + argument_vector[argument_count - 1] = p; +} + +static void +serve_global_option (char *arg) +{ + if (arg[0] != '-' || arg[1] == '\0' || arg[2] != '\0') + { + error_return: + if (alloc_pending (strlen (arg) + 80)) + sprintf (pending_error_text, + "E Protocol error: bad global option %s", + arg); + return; + } + switch (arg[1]) + { + case 'l': + error(0, 0, "WARNING: global `-l' option ignored."); + break; + case 'n': + noexec = 1; + logoff = 1; + break; + case 'q': + quiet = 1; + break; + case 'r': + cvswrite = 0; + break; + case 'Q': + really_quiet = 1; + break; + case 't': + trace++; + break; + default: + goto error_return; + } +} + +static void +serve_set (char *arg) +{ + /* FIXME: This sends errors immediately (I think); they should be + put into pending_error. */ + variable_set (arg); +} + +#ifdef ENCRYPTION + +#ifdef HAVE_KERBEROS + +static void +serve_kerberos_encrypt( char *arg ) +{ + /* 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 */ + +#ifdef HAVE_GSSAPI + +static void +serve_gssapi_encrypt( char *arg ) +{ + if (cvs_gssapi_wrapping) + { + /* We're already using a gssapi_wrap buffer for stream + authentication. Flush everything we've output so far, and + turn on encryption for future data. On the input side, we + should only have unwrapped as far as the Gssapi-encrypt + command, so future unwrapping will become encrypted. */ + buf_flush (buf_to_net, 1); + cvs_gssapi_encrypt = 1; + return; + } + + /* All future communication with the client will be encrypted. */ + + cvs_gssapi_encrypt = 1; + + buf_to_net = cvs_gssapi_wrap_buffer_initialize (buf_to_net, 0, + gcontext, + buf_to_net->memory_error); + buf_from_net = cvs_gssapi_wrap_buffer_initialize (buf_from_net, 1, + gcontext, + buf_from_net->memory_error); + + cvs_gssapi_wrapping = 1; +} + +#endif /* HAVE_GSSAPI */ + +#endif /* ENCRYPTION */ + +#ifdef HAVE_GSSAPI + +static void +serve_gssapi_authenticate (char *arg) +{ + if (cvs_gssapi_wrapping) + { + /* We're already using a gssapi_wrap buffer for encryption. + That includes authentication, so we don't have to do + anything further. */ + return; + } + + buf_to_net = cvs_gssapi_wrap_buffer_initialize (buf_to_net, 0, + gcontext, + buf_to_net->memory_error); + buf_from_net = cvs_gssapi_wrap_buffer_initialize (buf_from_net, 1, + gcontext, + buf_from_net->memory_error); + + cvs_gssapi_wrapping = 1; +} + +#endif /* HAVE_GSSAPI */ + + + +#ifdef SERVER_FLOWCONTROL +/* The maximum we'll queue to the remote client before blocking. */ +# ifndef SERVER_HI_WATER +# define SERVER_HI_WATER (2 * 1024 * 1024) +# endif /* SERVER_HI_WATER */ +/* When the buffer drops to this, we restart the child */ +# ifndef SERVER_LO_WATER +# define SERVER_LO_WATER (1 * 1024 * 1024) +# endif /* SERVER_LO_WATER */ +#endif /* SERVER_FLOWCONTROL */ + + + +static void +serve_questionable (char *arg) +{ + static int initted; + + if (!initted) + { + /* Pick up ignores from CVSROOTADM_IGNORE, $HOME/.cvsignore on server, + and CVSIGNORE on server. */ + ign_setup (); + initted = 1; + } + + if (dir_name == NULL) + { + buf_output0 (buf_to_net, "E Protocol error: 'Directory' missing"); + return; + } + + if (outside_dir (arg)) + return; + + if (!ign_name (arg)) + { + char *update_dir; + + 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, arg); + buf_output (buf_to_net, "\n", 1); + } +} + + + +static struct buffer *protocol = NULL; + +/* 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; + +/* Likewise, but stuff which will go to stderr. */ +static struct buffer *saved_outerr; + + + +static void +protocol_memory_error (struct buffer *buf) +{ + error (1, ENOMEM, "Virtual memory exhausted"); +} + + + +/* + * Process IDs of the subprocess, or negative if that subprocess + * does not exist. + */ +static pid_t command_pid; + +static void +outbuf_memory_error (struct buffer *buf) +{ + static const char msg[] = "E Fatal server error\n\ +error ENOMEM Virtual memory exhausted.\n"; + if (command_pid > 0) + kill (command_pid, SIGTERM); + + /* + * We have arranged things so that printing this now either will + * be valid, or the "E fatal error" line will get glommed onto the + * end of an existing "E" or "M" response. + */ + + /* If this gives an error, not much we could do. syslog() it? */ + write (STDOUT_FILENO, msg, sizeof (msg) - 1); +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, "virtual memory exhausted"); +#endif + exit (EXIT_FAILURE); +} + +static void +input_memory_error (struct buffer *buf) +{ + outbuf_memory_error (buf); +} + + + +/* If command is valid, return 1. + * Else if command is invalid and croak_on_invalid is set, then die. + * Else just return 0 to indicate that command is invalid. + */ +static int +check_command_valid_p (char *cmd_name) +{ + /* Right now, only pserver notices invalid 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 (current_parsed_root->directory) + + strlen (CVSROOTADM) + + strlen (CVSROOTADM_READERS) + + 3; + + fname = xmalloc (flen); + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_READERS); + + fp = fopen (fname, "r"); + + if (fp == NULL) + { + if (!existence_error (errno)) + { + /* Need to deny access, so that attackers can't fool + us with some sort of denial of service attack. */ + error (0, errno, "cannot open %s", fname); + free (fname); + return 0; + } + } + 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 (num_red > 0 && linebuf[num_red - 1] == '\n') + linebuf[num_red - 1] = '\0'; + + if (strcmp (linebuf, CVS_Username) == 0) + goto handle_invalid; + } + if (num_red < 0 && !feof (fp)) + error (0, errno, "cannot read %s", fname); + + /* If not listed specifically as a reader, then this user + has write access by default unless writers are also + specified in a file . */ + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", fname); + } + free (fname); + + /* Now check the writers file. */ + + flen = strlen (current_parsed_root->directory) + + strlen (CVSROOTADM) + + strlen (CVSROOTADM_WRITERS) + + 3; + + fname = xmalloc (flen); + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_WRITERS); + + fp = fopen (fname, "r"); + + if (fp == NULL) + { + if (linebuf) + free (linebuf); + if (existence_error (errno)) + { + /* Writers file does not exist, so everyone is a writer, + by default. */ + free (fname); + return 1; + } + else + { + /* Need to deny access, so that attackers can't fool + us with some sort of denial of service attack. */ + error (0, errno, "cannot read %s", fname); + free (fname); + return 0; + } + } + + found_it = 0; + while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + /* Chop newline by hand, for strcmp()'s sake. */ + if (num_red > 0 && linebuf[num_red - 1] == '\n') + linebuf[num_red - 1] = '\0'; + + if (strcmp (linebuf, CVS_Username) == 0) + { + found_it = 1; + break; + } + } + if (num_red < 0 && !feof (fp)) + error (0, errno, "cannot read %s", fname); + + if (found_it) + { + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", fname); + if (linebuf) + free (linebuf); + free (fname); + return 1; + } + else /* writers file exists, but this user not listed in it */ + { + handle_invalid: + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", fname); + if (linebuf) + free (linebuf); + free (fname); + return 0; + } + } +#endif /* AUTH_SERVER_SUPPORT */ + + /* If ever reach end of this function, command must be valid. */ + 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; +#ifdef SUNOS_KLUDGE +static int max_command_fd; +#endif + +#ifdef SERVER_FLOWCONTROL +static int flowcontrol_pipe[2]; +#endif /* SERVER_FLOWCONTROL */ + + + +/* + * Set buffer FD to non-blocking I/O. Returns 0 for success or errno + * code. + */ +int +set_nonblock_fd (fd) + int fd; +{ + int flags; + + flags = fcntl (fd, F_GETFL, 0); + if (flags < 0) + return errno; + if (fcntl (fd, F_SETFL, flags | O_NONBLOCK) < 0) + return errno; + return 0; +} + + + +static void +do_cvs_command (char *cmd_name, int (*command) (int, char **)) +{ + /* + * The following file descriptors are set to -1 if that file is not + * currently open. + */ + + /* Data on these pipes is a series of '\n'-terminated lines. */ + int stdout_pipe[2]; + int stderr_pipe[2]; + + /* + * Data on this pipe is a series of counted (see buf_send_counted) + * packets. Each packet must be processed atomically (i.e. not + * interleaved with data from stdout_pipe or stderr_pipe). + */ + int protocol_pipe[2]; + + int dev_null_fd = -1; + + int errs; + + TRACE (TRACE_FUNCTION, "do_cvs_command (%s)", cmd_name); + + command_pid = -1; + stdout_pipe[0] = -1; + stdout_pipe[1] = -1; + stderr_pipe[0] = -1; + stderr_pipe[1] = -1; + protocol_pipe[0] = -1; + protocol_pipe[1] = -1; + + server_write_entries (); + + if (print_pending_error ()) + goto free_args_and_return; + + /* Global `cvs_cmd_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 valid for + this user. */ + if (!check_command_valid_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; + } + cvs_cmd_name = cmd_name; + + (void) server_notify (); + + /* + * We use a child process which actually does the operation. This + * is so we can intercept its standard output. Even if all of CVS + * were written to go to some special routine instead of writing + * to stdout or stderr, we would still need to do the same thing + * for the RCS commands. + */ + + if (pipe (stdout_pipe) < 0) + { + buf_output0 (buf_to_net, "E pipe failed\n"); + print_error (errno); + goto error_exit; + } + if (pipe (stderr_pipe) < 0) + { + buf_output0 (buf_to_net, "E pipe failed\n"); + print_error (errno); + goto error_exit; + } + if (pipe (protocol_pipe) < 0) + { + buf_output0 (buf_to_net, "E pipe failed\n"); + print_error (errno); + goto error_exit; + } +#ifdef SERVER_FLOWCONTROL + if (pipe (flowcontrol_pipe) < 0) + { + buf_output0 (buf_to_net, "E pipe failed\n"); + print_error (errno); + goto error_exit; + } + set_nonblock_fd (flowcontrol_pipe[0]); + set_nonblock_fd (flowcontrol_pipe[1]); +#endif /* SERVER_FLOWCONTROL */ + + dev_null_fd = CVS_OPEN (DEVNULL, O_RDONLY); + if (dev_null_fd < 0) + { + buf_output0 (buf_to_net, "E open /dev/null failed\n"); + 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. */ + /* FIXME: appending a newline, rather than using "MT" as we + do in the child process, is probably not really a very good + way to "handle" them. */ + 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) + { + buf_output0 (buf_to_net, "E fork failed\n"); + print_error (errno); + goto error_exit; + } + if (command_pid == 0) + { + int exitstatus; + + /* Since we're in the child, and the parent is going to take + care of packaging up our error messages, we can clear this + flag. */ + error_use_protocol = 0; + + 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. */ + if (buf_to_net != NULL) + { + buf_free (buf_to_net); + buf_to_net = NULL; + } + if (buf_from_net != NULL) + { + buf_free (buf_from_net); + buf_from_net = NULL; + } + + /* 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"); + if (dup2 (stdout_pipe[1], STDOUT_FILENO) < 0) + error (1, errno, "can't set up pipes"); + if (dup2 (stderr_pipe[1], STDERR_FILENO) < 0) + error (1, errno, "can't set up pipes"); + close (dev_null_fd); + close (stdout_pipe[0]); + close (stdout_pipe[1]); + close (stderr_pipe[0]); + close (stderr_pipe[1]); + close (protocol_pipe[0]); + close_on_exec (protocol_pipe[1]); +#ifdef SERVER_FLOWCONTROL + close_on_exec (flowcontrol_pipe[0]); + close (flowcontrol_pipe[1]); +#endif /* SERVER_FLOWCONTROL */ + + /* + * Set this in .bashrc if you want to give yourself time to attach + * to the subprocess with a debugger. + */ + if (getenv ("CVS_SERVER_SLEEP")) + { + int secs = atoi (getenv ("CVS_SERVER_SLEEP")); + TRACE (TRACE_DATA, "Sleeping CVS_SERVER_SLEEP (%d) seconds", secs); + sleep (secs); + } + else + TRACE (TRACE_DATA, "CVS_SERVER_SLEEP not set."); + + exitstatus = (*command) (argument_count, argument_vector); + + /* Output any partial lines. If the client doesn't support + "MT", we go ahead and just tack on a newline since the + protocol doesn't support anything better. */ + if (! buf_empty_p (saved_output)) + { + buf_output0 (protocol, supported_response ("MT") ? "MT text " : "M "); + buf_append_buffer (protocol, saved_output); + buf_output (protocol, "\n", 1); + buf_send_counted (protocol); + } + /* For now we just discard partial lines on stderr. I suspect + that CVS can't write such lines unless there is a bug. */ + + buf_free (protocol); + + /* Close the pipes explicitly in order to send an EOF to the parent, + * then wait for the parent to close the flow control pipe. This + * avoids a race condition where a child which dumped more than the + * high water mark into the pipes could complete its job and exit, + * leaving the parent process to attempt to write a stop byte to the + * closed flow control pipe, which earned the parent a SIGPIPE, which + * it normally only expects on the network pipe and that causes it to + * exit with an error message, rather than the SIGCHILD that it knows + * how to handle correctly. + */ + /* Let exit() close STDIN - it's from /dev/null anyhow. */ + fclose (stderr); + fclose (stdout); + close (protocol_pipe[1]); +#ifdef SERVER_FLOWCONTROL + { + char junk; + ssize_t status; + while ((status = read (flowcontrol_pipe[0], &junk, 1)) > 0 + || (status == -1 && errno == EAGAIN)); + } + /* FIXME: No point in printing an error message with error(), + * as STDERR is already closed, but perhaps this could be syslogged? + */ +#endif + + exit (exitstatus); + } + + /* OK, sit around getting all the input from the child. */ + { + 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 = 1; +#ifdef SERVER_FLOWCONTROL + int have_flowcontrolled = 0; +#endif /* SERVER_FLOWCONTROL */ + + FD_ZERO (&command_fds_to_drain.fds); + num_to_check = stdout_pipe[0]; + FD_SET (stdout_pipe[0], &command_fds_to_drain.fds); + if (stderr_pipe[0] > num_to_check) + num_to_check = stderr_pipe[0]; + FD_SET (stderr_pipe[0], &command_fds_to_drain.fds); + if (protocol_pipe[0] > num_to_check) + num_to_check = protocol_pipe[0]; + FD_SET (protocol_pipe[0], &command_fds_to_drain.fds); + if (STDOUT_FILENO > num_to_check) + num_to_check = STDOUT_FILENO; +#ifdef SUNOS_KLUDGE + max_command_fd = num_to_check; +#endif + /* + * File descriptors are numbered from 0, so num_to_check needs to + * be one larger than the largest descriptor. + */ + ++num_to_check; + if (num_to_check > FD_SETSIZE) + { + buf_output0 (buf_to_net, + "E internal error: FD_SETSIZE not big enough.\n\ +error \n"); + goto error_exit; + } + + 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) + { + buf_output0 (buf_to_net, "E close failed\n"); + print_error (errno); + goto error_exit; + } + stdout_pipe[1] = -1; + + if (close (stderr_pipe[1]) < 0) + { + buf_output0 (buf_to_net, "E close failed\n"); + print_error (errno); + goto error_exit; + } + stderr_pipe[1] = -1; + + if (close (protocol_pipe[1]) < 0) + { + buf_output0 (buf_to_net, "E close failed\n"); + print_error (errno); + goto error_exit; + } + protocol_pipe[1] = -1; + +#ifdef SERVER_FLOWCONTROL + if (close (flowcontrol_pipe[0]) < 0) + { + buf_output0 (buf_to_net, "E close failed\n"); + print_error (errno); + goto error_exit; + } + flowcontrol_pipe[0] = -1; +#endif /* SERVER_FLOWCONTROL */ + + if (close (dev_null_fd) < 0) + { + buf_output0 (buf_to_net, "E close failed\n"); + print_error (errno); + goto error_exit; + } + dev_null_fd = -1; + + while (stdout_pipe[0] >= 0 + || stderr_pipe[0] >= 0 + || protocol_pipe[0] >= 0 + || count_needed <= 0) + { + fd_set readfds; + fd_set writefds; + int numfds; + struct timeval *timeout_ptr; + struct timeval timeout; +#ifdef SERVER_FLOWCONTROL + int bufmemsize; + + /* + * 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); + if (!have_flowcontrolled && (bufmemsize > SERVER_HI_WATER)) + { + if (write(flowcontrol_pipe[1], "S", 1) == 1) + have_flowcontrolled = 1; + } + else if (have_flowcontrolled && (bufmemsize < SERVER_LO_WATER)) + { + if (write(flowcontrol_pipe[1], "G", 1) == 1) + have_flowcontrolled = 0; + } +#endif /* SERVER_FLOWCONTROL */ + + FD_ZERO (&readfds); + FD_ZERO (&writefds); + + if (count_needed <= 0) + { + /* there is data pending which was read from the protocol pipe + * so don't block if we don't find any data + */ + timeout.tv_sec = 0; + timeout.tv_usec = 0; + timeout_ptr = &timeout; + } + else + { + /* block indefinately */ + timeout_ptr = NULL; + } + + if (! buf_empty_p (buf_to_net)) + FD_SET (STDOUT_FILENO, &writefds); + + if (stdout_pipe[0] >= 0) + { + FD_SET (stdout_pipe[0], &readfds); + } + if (stderr_pipe[0] >= 0) + { + FD_SET (stderr_pipe[0], &readfds); + } + if (protocol_pipe[0] >= 0) + { + 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 + SCO doesn't let you select on exceptions on pipes. */ + numfds = select (num_to_check, &readfds, &writefds, + (fd_set *)0, timeout_ptr); + if (numfds < 0 + && errno != EINTR) + { + buf_output0 (buf_to_net, "E select failed\n"); + print_error (errno); + goto error_exit; + } + } while (numfds < 0); + + if (numfds == 0) + { + FD_ZERO (&readfds); + FD_ZERO (&writefds); + } + + if (FD_ISSET (STDOUT_FILENO, &writefds)) + { + /* What should we do with errors? syslog() them? */ + buf_send_output (buf_to_net); + } + + if (protocol_pipe[0] >= 0 + && (FD_ISSET (protocol_pipe[0], &readfds))) + { + int status; + int count_read; + + status = buf_input_data (protocol_inbuf, &count_read); + + if (status == -1) + { + close (protocol_pipe[0]); + protocol_pipe[0] = -1; + } + else if (status > 0) + { + buf_output0 (buf_to_net, "E buf_input_data failed\n"); + print_error (status); + goto error_exit; + } + + /* + * We only call buf_copy_counted if we have read + * enough bytes to make it worthwhile. This saves us + * from continually recounting the amount of data we + * have. + */ + count_needed -= count_read; + } + /* this is still part of the protocol pipe procedure, but it is + * outside the above conditional so that unprocessed data can be + * left in the buffer and stderr/stdout can be read when a flush + * signal is received and control can return here without passing + * through the select code and maybe blocking + */ + while (count_needed <= 0) + { + int special = 0; + + 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 <0, it means that the child + * wants us to flush the pipe & maybe stderr or stdout. + * + * After that we break to read stderr & stdout again before + * going back to the protocol pipe + * + * Upon breaking, count_needed = 0, so the next pass will only + * perform a non-blocking select before returning here to finish + * processing data we already read from the protocol buffer + */ + if (special == -1) + { + cvs_flushout(); + break; + } + if (special == -2) + { + /* If the client supports the 'F' command, we send it. */ + if (supported_response ("F")) + { + buf_append_char (buf_to_net, 'F'); + buf_append_char (buf_to_net, '\n'); + } + cvs_flusherr (); + break; + } + } + + if (stdout_pipe[0] >= 0 + && (FD_ISSET (stdout_pipe[0], &readfds))) + { + int status; + + status = buf_input_data (stdoutbuf, (int *) NULL); + + buf_copy_lines (buf_to_net, stdoutbuf, 'M'); + + if (status == -1) + { + close (stdout_pipe[0]); + stdout_pipe[0] = -1; + } + else if (status > 0) + { + buf_output0 (buf_to_net, "E buf_input_data failed\n"); + print_error (status); + goto error_exit; + } + + /* What should we do with errors? syslog() them? */ + buf_send_output (buf_to_net); + } + + if (stderr_pipe[0] >= 0 + && (FD_ISSET (stderr_pipe[0], &readfds))) + { + int status; + + status = buf_input_data (stderrbuf, (int *) NULL); + + buf_copy_lines (buf_to_net, stderrbuf, 'E'); + + if (status == -1) + { + close (stderr_pipe[0]); + stderr_pipe[0] = -1; + } + else if (status > 0) + { + buf_output0 (buf_to_net, "E buf_input_data failed\n"); + print_error (status); + goto error_exit; + } + + /* What should we do with errors? syslog() them? */ + buf_send_output (buf_to_net); + } + } + + /* + * OK, we've gotten EOF on all the pipes. If there is + * anything left on stdoutbuf or stderrbuf (this could only + * happen if there was no trailing newline), send it over. + */ + if (! buf_empty_p (stdoutbuf)) + { + buf_append_char (stdoutbuf, '\n'); + buf_copy_lines (buf_to_net, stdoutbuf, 'M'); + } + if (! buf_empty_p (stderrbuf)) + { + buf_append_char (stderrbuf, '\n'); + buf_copy_lines (buf_to_net, stderrbuf, 'E'); + } + if (! buf_empty_p (protocol_inbuf)) + buf_output0 (buf_to_net, + "E Protocol error: uncounted data discarded\n"); + +#ifdef SERVER_FLOWCONTROL + close (flowcontrol_pipe[1]); + flowcontrol_pipe[1] = -1; +#endif /* SERVER_FLOWCONTROL */ + + errs = 0; + + while (command_pid > 0) + { + int status; + pid_t waited_pid; + waited_pid = waitpid (command_pid, &status, 0); + if (waited_pid < 0) + { + /* + * Intentionally ignoring EINTR. Other errors + * "can't happen". + */ + continue; + } + + if (WIFEXITED (status)) + errs += WEXITSTATUS (status); + 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 + * signal names (either of the "Terminated" or the "SIGTERM" + * variety). But cvs doesn't currently use libiberty...we + * could roll our own.... FIXME. + */ + 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. */ + if (WCOREDUMP (status)) + { + 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; + } + if (waited_pid == command_pid) + command_pid = -1; + } + + /* + * 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_flush (buf_to_net, 1); + buf_shutdown (protocol_inbuf); + buf_free (protocol_inbuf); + protocol_inbuf = NULL; + buf_shutdown (stderrbuf); + buf_free (stderrbuf); + stderrbuf = NULL; + buf_shutdown (stdoutbuf); + buf_free (stdoutbuf); + stdoutbuf = NULL; + } + + if (errs) + /* We will have printed an error message already. */ + buf_output0 (buf_to_net, "error \n"); + else + buf_output0 (buf_to_net, "ok\n"); + goto free_args_and_return; + + error_exit: + if (command_pid > 0) + kill (command_pid, SIGTERM); + + while (command_pid > 0) + { + pid_t waited_pid; + waited_pid = waitpid (command_pid, (int *) 0, 0); + if (waited_pid < 0 && errno == EINTR) + continue; + if (waited_pid == command_pid) + command_pid = -1; + } + + close (dev_null_fd); + close (protocol_pipe[0]); + close (protocol_pipe[1]); + close (stderr_pipe[0]); + close (stderr_pipe[1]); + close (stdout_pipe[0]); + close (stdout_pipe[1]); +#ifdef SERVER_FLOWCONTROL + close (flowcontrol_pipe[0]); + close (flowcontrol_pipe[1]); +#endif /* SERVER_FLOWCONTROL */ + + free_args_and_return: + /* Now free the arguments. */ + { + /* argument_vector[0] is a dummy argument, we don't mess with it. */ + char **cp; + for (cp = argument_vector + 1; + cp < argument_vector + argument_count; + ++cp) + free (*cp); + + argument_count = 1; + } + + /* Flush out any data not yet sent. */ + set_block (buf_to_net); + buf_flush (buf_to_net, 1); + + return; +} + +#ifdef SERVER_FLOWCONTROL +/* + * Called by the child at convenient points in the server's execution for + * the server child to block.. ie: when it has no locks active. + */ +void +server_pause_check(void) +{ + int paused = 0; + char buf[1]; + + while (read (flowcontrol_pipe[0], buf, 1) == 1) + { + if (*buf == 'S') /* Stop */ + paused = 1; + else if (*buf == 'G') /* Go */ + paused = 0; + else + return; /* ??? */ + } + while (paused) { + int numfds, numtocheck; + fd_set fds; + + FD_ZERO (&fds); + FD_SET (flowcontrol_pipe[0], &fds); + numtocheck = flowcontrol_pipe[0] + 1; + + do { + numfds = select (numtocheck, &fds, (fd_set *)0, + (fd_set *)0, (struct timeval *)NULL); + if (numfds < 0 + && errno != EINTR) + { + buf_output0 (buf_to_net, "E select failed\n"); + print_error (errno); + return; + } + } while (numfds < 0); + + if (FD_ISSET (flowcontrol_pipe[0], &fds)) + { + int got; + + while ((got = read (flowcontrol_pipe[0], buf, 1)) == 1) + { + if (*buf == 'S') /* Stop */ + paused = 1; + else if (*buf == 'G') /* Go */ + paused = 0; + 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 (const char *update_dir, const char *repository) +{ + if (server_dir != NULL) + { + buf_output0 (protocol, server_dir); + 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, "/"); +} + + + +/* + * Entries line that we are squirreling away to send to the client when + * we are ready. + */ +static char *entries_line; + +/* + * File which has been Scratch_File'd, we are squirreling away that fact + * to inform the client when we are ready. + */ +static char *scratched_file; + +/* + * The scratched_file will need to be removed as well as having its entry + * removed. + */ +static int kill_scratched_file; + + + +void +server_register (const char *name, const char *version, const char *timestamp, + const char *options, const char *tag, const char *date, + const char *conflict) +{ + int len; + + if (options == NULL) + options = ""; + + TRACE ( 1, "server_register(%s, %s, %s, %s, %s, %s, %s)", + name, version, timestamp ? timestamp : "", options, + tag ? tag : "", date ? date : "", + conflict ? conflict : "" ); + + if (entries_line != NULL) + { + /* + * If CVS decides to Register it more than once (which happens + * on "cvs update foo/foo.c" where foo and foo.c are already + * checked out), use the last of the entries lines Register'd. + */ + free (entries_line); + } + + /* + * I have reports of Scratch_Entry and Register both happening, in + * two different cases. Using the last one which happens is almost + * surely correct; I haven't tracked down why they both happen (or + * even verified that they are for the same file). + */ + if (scratched_file != NULL) + { + free (scratched_file); + scratched_file = NULL; + } + + len = (strlen (name) + strlen (version) + strlen (options) + 80); + if (tag) + len += strlen (tag); + if (date) + len += strlen (date); + + entries_line = xmalloc (len); + sprintf (entries_line, "/%s/%s/", name, version); + if (conflict != NULL) + { + strcat (entries_line, "+="); + } + strcat (entries_line, "/"); + strcat (entries_line, options); + strcat (entries_line, "/"); + if (tag != NULL) + { + strcat (entries_line, "T"); + strcat (entries_line, tag); + } + else if (date != NULL) + { + strcat (entries_line, "D"); + strcat (entries_line, date); + } +} + + + +void +server_scratch (const char *fname) +{ + /* + * I have reports of Scratch_Entry and Register both happening, in + * two different cases. Using the last one which happens is almost + * surely correct; I haven't tracked down why they both happen (or + * even verified that they are for the same file). + * + * Don't know if this is what whoever wrote the above comment was + * talking about, but this can happen in the case where a join + * removes a file - the call to Register puts the '-vers' into the + * Entries file after the file is removed + */ + if (entries_line != NULL) + { + free (entries_line); + entries_line = NULL; + } + + if (scratched_file != NULL) + { + buf_output0 (protocol, + "E CVS server internal error: duplicate Scratch_Entry\n"); + buf_send_counted (protocol); + return; + } + scratched_file = xstrdup (fname); + kill_scratched_file = 1; +} + +void +server_scratch_entry_only (void) +{ + kill_scratched_file = 0; +} + +/* Print a new entries line, from a previous server_register. */ +static void +new_entries_line (void) +{ + if (entries_line) + { + buf_output0 (protocol, entries_line); + buf_output (protocol, "\n", 1); + } + else + /* Return the error message as the Entries line. */ + buf_output0 (protocol, + "CVS server internal error: Register missing\n"); + free (entries_line); + entries_line = NULL; +} + + + +static void +serve_ci (char *arg) +{ + do_cvs_command ("commit", commit); +} + + + +static void +checked_in_response (const char *file, const char *update_dir, + const char *repository) +{ + if (supported_response ("Mode")) + { + struct stat sb; + char *mode_string; + + 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. */ + if (!existence_error (errno)) + error (0, errno, "cannot stat %s", file); + } + else + { + buf_output0 (protocol, "Mode "); + mode_string = mode_to_string (sb.st_mode); + buf_output0 (protocol, mode_string); + buf_output0 (protocol, "\n"); + free (mode_string); + } + } + + buf_output0 (protocol, "Checked-in "); + output_dir (update_dir, repository); + buf_output0 (protocol, file); + buf_output (protocol, "\n", 1); + new_entries_line (); +} + +void +server_checked_in (const char *file, const char *update_dir, + const char *repository) +{ + if (noexec) + return; + if (scratched_file != NULL && entries_line == NULL) + { + /* + * 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 "); + output_dir (update_dir, repository); + buf_output0 (protocol, file); + buf_output (protocol, "\n", 1); + free (scratched_file); + scratched_file = NULL; + } + else + { + checked_in_response (file, update_dir, repository); + } + buf_send_counted (protocol); +} + +void +server_update_entries (const char *file, const char *update_dir, + const char *repository, + enum server_updated_arg4 updated) +{ + if (noexec) + return; + if (updated == SERVER_UPDATED) + checked_in_response (file, update_dir, repository); + else + { + if (!supported_response ("New-entry")) + return; + buf_output0 (protocol, "New-entry "); + output_dir (update_dir, repository); + buf_output0 (protocol, file); + buf_output (protocol, "\n", 1); + new_entries_line (); + } + + buf_send_counted (protocol); +} + +static void +serve_update (char *arg) +{ + do_cvs_command ("update", update); +} + +static void +serve_diff (char *arg) +{ + do_cvs_command ("diff", diff); +} + +static void +serve_log (char *arg) +{ + do_cvs_command ("log", cvslog); +} + +static void +serve_rlog (char *arg) +{ + do_cvs_command ("rlog", cvslog); +} + +static void +serve_ls (char *arg) +{ + do_cvs_command ("ls", ls); +} + +static void +serve_rls (char *arg) +{ + do_cvs_command ("rls", ls); +} + +static void +serve_add (char *arg) +{ + do_cvs_command ("add", add); +} + +static void +serve_remove (char *arg) +{ + do_cvs_command ("remove", cvsremove); +} + +static void +serve_status (char *arg) +{ + do_cvs_command ("status", cvsstatus); +} + +static void +serve_rdiff (char *arg) +{ + do_cvs_command ("rdiff", patch); +} + +static void +serve_tag (char *arg) +{ + do_cvs_command ("tag", cvstag); +} + +static void +serve_rtag (char *arg) +{ + do_cvs_command ("rtag", cvstag); +} + +static void +serve_import (char *arg) +{ + do_cvs_command ("import", import); +} + +static void +serve_admin (char *arg) +{ + do_cvs_command ("admin", admin); +} + +static void +serve_history (char *arg) +{ + do_cvs_command ("history", history); +} + +static void +serve_release (char *arg) +{ + do_cvs_command ("release", release); +} + +static void serve_watch_on (char *); + +static void +serve_watch_on (char *arg) +{ + do_cvs_command ("watch", watch_on); +} + +static void serve_watch_off (char *); + +static void +serve_watch_off (char *arg) +{ + do_cvs_command ("watch", watch_off); +} + +static void serve_watch_add (char *); + +static void +serve_watch_add (char *arg) +{ + do_cvs_command ("watch", watch_add); +} + +static void serve_watch_remove (char *); + +static void +serve_watch_remove (char *arg) +{ + do_cvs_command ("watch", watch_remove); +} + +static void serve_watchers (char *); + +static void +serve_watchers (char *arg) +{ + do_cvs_command ("watchers", watchers); +} + +static void serve_editors (char *); + +static void +serve_editors (char *arg) +{ + do_cvs_command ("editors", editors); +} + +static void serve_noop (char *); + +static void +serve_noop (char *arg) +{ + + server_write_entries (); + if (!print_pending_error ()) + { + (void) server_notify (); + buf_output0 (buf_to_net, "ok\n"); + } + buf_flush (buf_to_net, 1); +} + +static void serve_version (char *); + +static void +serve_version (char *arg) +{ + do_cvs_command ("version", version); +} + +static void serve_init (char *); + +static void +serve_init (char *arg) +{ + cvsroot_t *saved_parsed_root; + + if (!isabsolute (arg)) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E init %s must be an absolute pathname", arg); + } +#ifdef AUTH_SERVER_SUPPORT + else if (Pserver_Repos != NULL) + { + if (strcmp (Pserver_Repos, arg) != 0) + { + if (alloc_pending (80 + strlen (Pserver_Repos) + strlen (arg))) + /* The explicitness is to aid people who are writing clients. + I don't see how this information could help an + attacker. */ + sprintf (pending_error_text, "\ +E Protocol error: init says \"%s\" but pserver says \"%s\"", + arg, Pserver_Repos); + } + } +#endif + + if (print_pending_error ()) + return; + + saved_parsed_root = current_parsed_root; + current_parsed_root = local_cvsroot (arg); + do_cvs_command ("init", init); + free_cvsroot_t (current_parsed_root); + current_parsed_root = saved_parsed_root; +} + +static void serve_annotate (char *); + +static void +serve_annotate (char *arg) +{ + do_cvs_command ("annotate", annotate); +} + +static void serve_rannotate (char *); + +static void +serve_rannotate (char *arg) +{ + do_cvs_command ("rannotate", annotate); +} + +static void +serve_co (char *arg) +{ + char *tempdir; + int status; + + if (print_pending_error ()) + return; + + if (!isdir (CVSADM)) + { + /* + * The client has not sent a "Repository" line. Check out + * into a pristine directory. + */ + tempdir = xmalloc (strlen (server_temp_dir) + 80); + if (tempdir == NULL) + { + buf_output0 (buf_to_net, "E Out of memory\n"); + return; + } + strcpy (tempdir, server_temp_dir); + strcat (tempdir, "/checkout-dir"); + status = mkdir_p (tempdir); + if (status != 0 && status != EEXIST) + { + 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 ( CVS_CHDIR (tempdir) < 0) + { + 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); + } + + /* Compensate for server_export()'s setting of cvs_cmd_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 (cvs_cmd_name, "export") == 0) ? + "export" : "checkout", + checkout); +} + +static void +serve_export (char *arg) +{ + /* Tell checkout() to behave like export not checkout. */ + cvs_cmd_name = "export"; + serve_co (arg); +} + + + +void +server_copy_file (const char *file, const char *update_dir, + const char *repository, const char *newfile) +{ + /* At least for now, our practice is to have the server enforce + noexec for the repository and the client enforce it for the + working directory. This might want more thought, and/or + documentation in cvsclient.texi (other responses do it + differently). */ + + if (!supported_response ("Copy-file")) + return; + 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"); +} + +/* See server.h for description. */ + +void +server_modtime (struct file_info *finfo, Vers_TS *vers_ts) +{ + char date[MAXDATELEN]; + char outdate[MAXDATELEN]; + + assert (vers_ts->vn_rcs != NULL); + + if (!supported_response ("Mod-time")) + return; + + if (RCS_getrevtime (finfo->rcs, vers_ts->vn_rcs, date, 0) == (time_t) -1) + /* FIXME? should we be printing some kind of warning? For one + thing I'm not 100% sure whether this happens in non-error + circumstances. */ + return; + date_to_internet (outdate, date); + buf_output0 (protocol, "Mod-time "); + buf_output0 (protocol, outdate); + buf_output0 (protocol, "\n"); +} + + + +/* See server.h for description. */ +void +server_updated ( + struct file_info *finfo, + Vers_TS *vers, + enum server_updated_arg4 updated, + mode_t mode, + unsigned char *checksum, + struct buffer *filebuf) +{ + if (noexec) + { + /* Hmm, maybe if we did the same thing for entries_file, we + could get rid of the kludges in server_register and + server_scratch which refrain from warning if both + Scratch_Entry and Register get called. Maybe. */ + if (scratched_file) + { + free (scratched_file); + scratched_file = NULL; + } + return; + } + + if (entries_line != NULL && scratched_file == NULL) + { + FILE *f; + struct buffer_data *list, *last; + unsigned long size; + char size_text[80]; + + /* The contents of the file will be in one of filebuf, + list/last, or here. */ + unsigned char *file; + size_t file_allocated; + size_t file_used; + + if (filebuf != NULL) + { + size = buf_length (filebuf); + if (mode == (mode_t) -1) + error (1, 0, "\ +CVS server internal error: no mode in server_updated"); + } + else + { + struct stat sb; + + if ( CVS_STAT (finfo->file, &sb) < 0) + { + if (existence_error (errno)) + { + /* 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. */ + free (entries_line); + entries_line = NULL; + goto done; + } + error (1, errno, "reading %s", finfo->fullname); + } + size = sb.st_size; + if (mode == (mode_t) -1) + { + /* FIXME: When we check out files the umask of the + server (set in .bashrc if rsh is in use) affects + what mode we send, and it shouldn't. */ + mode = sb.st_mode; + } + } + + if (checksum != NULL) + { + static int checksum_supported = -1; + + if (checksum_supported == -1) + { + checksum_supported = supported_response ("Checksum"); + } + + if (checksum_supported) + { + int i; + char buf[3]; + + buf_output0 (protocol, "Checksum "); + for (i = 0; i < 16; i++) + { + sprintf (buf, "%02x", (unsigned int) checksum[i]); + buf_output0 (protocol, buf); + } + buf_append_char (protocol, '\n'); + } + } + + if (updated == SERVER_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 = node->data; + free (entnode->timestamp); + entnode->timestamp = xstrdup ("="); + } + else if (updated == SERVER_MERGED) + buf_output0 (protocol, "Merged "); + else if (updated == SERVER_PATCHED) + buf_output0 (protocol, "Patched "); + else if (updated == SERVER_RCS_DIFF) + buf_output0 (protocol, "Rcs-diff "); + else + abort (); + output_dir (finfo->update_dir, finfo->repository); + buf_output0 (protocol, finfo->file); + buf_output (protocol, "\n", 1); + + new_entries_line (); + + { + char *mode_string; + + mode_string = mode_to_string (mode); + buf_output0 (protocol, mode_string); + buf_output0 (protocol, "\n"); + free (mode_string); + } + + list = last = NULL; + + file = NULL; + file_allocated = 0; + file_used = 0; + + if (size > 0) + { + /* Throughout this section we use binary mode to read the + file we are sending. The client handles any line ending + translation if necessary. */ + + if (file_gzip_level + /* + * For really tiny files, the gzip process startup + * time will outweigh the compression savings. This + * might be computable somehow; using 100 here is just + * a first approximation. + */ + && size > 100) + { + /* Basing this routine on read_and_gzip is not a + high-performance approach. But it seems easier + to code than the alternative (and less + vulnerable to subtle bugs). Given that this feature + is mainly for compatibility, that is the better + tradeoff. */ + + int fd; + + /* Callers must avoid passing us a buffer if + file_gzip_level is set. We could handle this case, + but it's not worth it since this case never arises + with a current client and server. */ + if (filebuf != NULL) + error (1, 0, "\ +CVS server internal error: unhandled case in server_updated"); + + fd = CVS_OPEN (finfo->file, O_RDONLY | OPEN_BINARY, 0); + if (fd < 0) + error (1, errno, "reading %s", finfo->fullname); + if (read_and_gzip (fd, finfo->fullname, &file, + &file_allocated, &file_used, + file_gzip_level)) + error (1, 0, "aborting due to compression error"); + size = file_used; + if (close (fd) < 0) + error (1, errno, "reading %s", finfo->fullname); + /* Prepending length with "z" is flag for using gzip here. */ + buf_output0 (protocol, "z"); + } + else if (filebuf == NULL) + { + long status; + + f = CVS_FOPEN (finfo->file, "rb"); + if (f == NULL) + error (1, errno, "reading %s", finfo->fullname); + status = buf_read_file (f, size, &list, &last); + if (status == -2) + (*protocol->memory_error) (protocol); + else if (status != 0) + error (1, ferror (f) ? errno : 0, "reading %s", + finfo->fullname); + if (fclose (f) == EOF) + error (1, errno, "reading %s", finfo->fullname); + } + } + + sprintf (size_text, "%lu\n", size); + buf_output0 (protocol, size_text); + + if (file != NULL) + { + buf_output (protocol, (char *) file, file_used); + free (file); + file = NULL; + } + else if (filebuf == NULL) + buf_append_data (protocol, list, last); + else + { + buf_append_buffer (protocol, filebuf); + } + /* Note we only send a newline here if the file ended with one. */ + + /* + * Avoid using up too much disk space for temporary files. + * A file which does not exist indicates that the file is up-to-date, + * which is now the case. If this is SERVER_MERGED, the file is + * 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 + || updated == SERVER_RCS_DIFF) + && filebuf == NULL + /* But if we are joining, we'll need the file when we call + join_file. */ + && !joining ()) + { + if (CVS_UNLINK (finfo->file) < 0) + error (0, errno, "cannot remove temp file for %s", + finfo->fullname); + } + } + else if (scratched_file != NULL && entries_line == NULL) + { + if (strcmp (scratched_file, finfo->file) != 0) + error (1, 0, + "CVS server internal error: `%s' vs. `%s' scratched", + scratched_file, + finfo->file); + free (scratched_file); + scratched_file = NULL; + + if (kill_scratched_file) + buf_output0 (protocol, "Removed "); + else + buf_output0 (protocol, "Remove-entry "); + output_dir (finfo->update_dir, finfo->repository); + buf_output0 (protocol, finfo->file); + buf_output (protocol, "\n", 1); + /* keep the vers structure up to date in case we do a join + * - if there isn't a file, it can't very well have a version number, can it? + * + * we do it here on the assumption that since we just told the client + * to remove the file/entry, it will, and we want to remember that. + * If it fails, that's the client's problem, not ours + */ + if (vers && vers->vn_user != NULL) + { + free (vers->vn_user); + vers->vn_user = NULL; + } + if (vers && vers->ts_user != NULL) + { + free (vers->ts_user); + vers->ts_user = NULL; + } + } + else if (scratched_file == NULL && entries_line == NULL) + { + /* + * This can happen with death support if we were processing + * a dead file in a checkout. + */ + } + else + error (1, 0, + "CVS server internal error: Register *and* Scratch_Entry.\n"); + buf_send_counted (protocol); + done:; +} + +/* Return whether we should send patches in RCS format. */ + +int +server_use_rcs_diff (void) +{ + return supported_response ("Rcs-diff"); +} + + + +void +server_set_entstat (const char *update_dir, const char *repository) +{ + static int set_static_supported = -1; + if (set_static_supported == -1) + set_static_supported = supported_response ("Set-static-directory"); + if (!set_static_supported) return; + + buf_output0 (protocol, "Set-static-directory "); + output_dir (update_dir, repository); + buf_output0 (protocol, "\n"); + buf_send_counted (protocol); +} + + + +void +server_clear_entstat (const char *update_dir, const char *repository) +{ + static int clear_static_supported = -1; + if (clear_static_supported == -1) + clear_static_supported = supported_response ("Clear-static-directory"); + if (!clear_static_supported) return; + + if (noexec) + return; + + buf_output0 (protocol, "Clear-static-directory "); + output_dir (update_dir, repository); + buf_output0 (protocol, "\n"); + buf_send_counted (protocol); +} + + + +void +server_set_sticky (const char *update_dir, const char *repository, + const char *tag, const 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; + + if (noexec) + return; + + if (tag == NULL && date == NULL) + { + buf_output0 (protocol, "Clear-sticky "); + output_dir (update_dir, repository); + buf_output0 (protocol, "\n"); + } + else + { + buf_output0 (protocol, "Set-sticky "); + output_dir (update_dir, repository); + buf_output0 (protocol, "\n"); + if (tag != NULL) + { + 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, "\n"); + } + buf_send_counted (protocol); +} + + + +struct template_proc_data +{ + const char *update_dir; + const char *repository; +}; + +static int +template_proc (const char *repository, const char *template, void *closure) +{ + FILE *fp; + char buf[1024]; + size_t n; + struct stat sb; + struct template_proc_data *data = (struct template_proc_data *)closure; + + if (!supported_response ("Template")) + /* Might want to warn the user that the rcsinfo feature won't work. */ + return 0; + buf_output0 (protocol, "Template "); + output_dir (data->update_dir, data->repository); + buf_output0 (protocol, "\n"); + + fp = CVS_FOPEN (template, "rb"); + if (fp == NULL) + { + error (0, errno, "Couldn't open rcsinfo template file %s", template); + return 1; + } + if (fstat (fileno (fp), &sb) < 0) + { + error (0, errno, "cannot stat rcsinfo template file %s", template); + return 1; + } + sprintf (buf, "%ld\n", (long) sb.st_size); + buf_output0 (protocol, buf); + while (!feof (fp)) + { + n = fread (buf, 1, sizeof buf, fp); + buf_output (protocol, buf, n); + if (ferror (fp)) + { + error (0, errno, "cannot read rcsinfo template file %s", template); + (void) fclose (fp); + return 1; + } + } + buf_send_counted (protocol); + if (fclose (fp) < 0) + error (0, errno, "cannot close rcsinfo template file %s", template); + return 0; +} + + + +void +server_clear_template (const char *update_dir, const char *repository) +{ + assert (update_dir != NULL); + + if (noexec) + return; + + if (!supported_response ("Clear-template") && + !supported_response ("Template")) + /* Might want to warn the user that the rcsinfo feature won't work. */ + return; + + if (supported_response ("Clear-template")) + { + buf_output0 (protocol, "Clear-template "); + output_dir (update_dir, repository); + buf_output0 (protocol, "\n"); + buf_send_counted (protocol); + } + else + { + buf_output0 (protocol, "Template "); + output_dir (update_dir, repository); + buf_output0 (protocol, "\n"); + buf_output0 (protocol, "0\n"); + buf_send_counted (protocol); + } +} + + + +void +server_template (const char *update_dir, const char *repository) +{ + struct template_proc_data data; + data.update_dir = update_dir; + data.repository = repository; + (void) Parse_Info (CVSROOTADM_RCSINFO, repository, template_proc, + PIOPT_ALL, &data); +} + + + +static void +serve_gzip_contents (char *arg) +{ + int level; + level = atoi (arg); + if (level == 0) + level = 6; + file_gzip_level = level; +} + +static void +serve_gzip_stream (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); +} + +/* Tell the client about RCS options set in CVSROOT/cvswrappers. */ +static void +serve_wrapper_sendme_rcs_options (char *arg) +{ + /* Actually, this is kind of sdrawkcab-ssa: the client wants + * verbatim lines from a cvswrappers file, but the server has + * already parsed the cvswrappers file into the wrap_list struct. + * Therefore, the server loops over wrap_list, unparsing each + * entry before sending it. + */ + char *wrapper_line = NULL; + + wrap_setup (); + + for (wrap_unparse_rcs_options (&wrapper_line, 1); + wrapper_line; + wrap_unparse_rcs_options (&wrapper_line, 0)) + { + buf_output0 (buf_to_net, "Wrapper-rcsOption "); + buf_output0 (buf_to_net, wrapper_line); + buf_output0 (buf_to_net, "\012");; + free (wrapper_line); + } + + buf_output0 (buf_to_net, "ok\012"); + + /* The client is waiting for us, so we better send the data now. */ + buf_flush (buf_to_net, 1); +} + + +static void +serve_ignore (char *arg) +{ + /* + * Just ignore this command. This is used to support the + * update-patches command, which is not a real command, but a signal + * to the client that update will accept the -u argument. + */ +} + +static int +expand_proc (int argc, char **argv, char *where, char *mwhere, char *mfile, int shorten, int local_specified, char *omodule, char *msg) +{ + int i; + char *dir = argv[0]; + + /* If mwhere has been specified, the thing we're expanding is a + module -- just return its name so the client will ask for the + right thing later. If it is an alias or a real directory, + mwhere will not be set, so send out the appropriate + expansion. */ + + if (mwhere != NULL) + { + buf_output0 (buf_to_net, "Module-expansion "); + if (server_dir != NULL) + { + buf_output0 (buf_to_net, server_dir); + buf_output0 (buf_to_net, "/"); + } + buf_output0 (buf_to_net, mwhere); + if (mfile != NULL) + { + buf_append_char (buf_to_net, '/'); + buf_output0 (buf_to_net, mfile); + } + buf_append_char (buf_to_net, '\n'); + } + else + { + /* We may not need to do this anymore -- check the definition + of aliases before removing */ + if (argc == 1) + { + buf_output0 (buf_to_net, "Module-expansion "); + if (server_dir != NULL) + { + buf_output0 (buf_to_net, server_dir); + buf_output0 (buf_to_net, "/"); + } + buf_output0 (buf_to_net, dir); + buf_append_char (buf_to_net, '\n'); + } + else + { + for (i = 1; i < argc; ++i) + { + buf_output0 (buf_to_net, "Module-expansion "); + if (server_dir != NULL) + { + buf_output0 (buf_to_net, server_dir); + buf_output0 (buf_to_net, "/"); + } + 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; +} + +static void +serve_expand_modules (char *arg) +{ + int i; + int err; + DBM *db; + err = 0; + + db = open_module (); + for (i = 1; i < argument_count; i++) + err += do_module (db, argument_vector[i], + CHECKOUT, "Updating", expand_proc, + NULL, 0, 0, 0, 0, + (char *) NULL); + close_module (db); + { + /* argument_vector[0] is a dummy argument, we don't mess with it. */ + char **cp; + for (cp = argument_vector + 1; + cp < argument_vector + argument_count; + ++cp) + free (*cp); + + argument_count = 1; + } + if (err) + /* We will have printed an error message already. */ + buf_output0 (buf_to_net, "error \n"); + else + 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); +} + + + +static void serve_valid_requests (char *arg); + +#endif /* SERVER_SUPPORT */ +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) + +/* + * Parts of this table are shared with the client code, + * but the client doesn't need to know about the handler + * functions. + */ + +struct request requests[] = +{ +#ifdef SERVER_SUPPORT +#define REQ_LINE(n, f, s) {n, f, s} +#else +#define REQ_LINE(n, f, s) {n, s} +#endif + + REQ_LINE("Root", serve_root, RQ_ESSENTIAL | RQ_ROOTLESS), + REQ_LINE("Valid-responses", serve_valid_responses, + RQ_ESSENTIAL | RQ_ROOTLESS), + REQ_LINE("valid-requests", serve_valid_requests, + RQ_ESSENTIAL | RQ_ROOTLESS), + REQ_LINE("Repository", serve_repository, 0), + REQ_LINE("Directory", serve_directory, RQ_ESSENTIAL), + REQ_LINE("Max-dotdot", serve_max_dotdot, 0), + REQ_LINE("Static-directory", serve_static_directory, 0), + REQ_LINE("Sticky", serve_sticky, 0), + REQ_LINE("Entry", serve_entry, RQ_ESSENTIAL), + REQ_LINE("Kopt", serve_kopt, 0), + REQ_LINE("Checkin-time", serve_checkin_time, 0), + REQ_LINE("Modified", serve_modified, RQ_ESSENTIAL), + REQ_LINE("Is-modified", serve_is_modified, 0), + + /* 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 | RQ_ROOTLESS), + + REQ_LINE("Unchanged", serve_unchanged, RQ_ESSENTIAL), + REQ_LINE("Notify", serve_notify, 0), + REQ_LINE("Questionable", serve_questionable, 0), + REQ_LINE("Argument", serve_argument, RQ_ESSENTIAL), + REQ_LINE("Argumentx", serve_argumentx, RQ_ESSENTIAL), + REQ_LINE("Global_option", serve_global_option, RQ_ROOTLESS), + REQ_LINE("Gzip-stream", serve_gzip_stream, 0), + REQ_LINE("wrapper-sendme-rcsOptions", + serve_wrapper_sendme_rcs_options, + 0), + REQ_LINE("Set", serve_set, RQ_ROOTLESS), +#ifdef ENCRYPTION +# ifdef HAVE_KERBEROS + REQ_LINE("Kerberos-encrypt", serve_kerberos_encrypt, 0), +# endif +# ifdef HAVE_GSSAPI + REQ_LINE("Gssapi-encrypt", serve_gssapi_encrypt, 0), +# endif +#endif +#ifdef HAVE_GSSAPI + REQ_LINE("Gssapi-authenticate", serve_gssapi_authenticate, 0), +#endif + REQ_LINE("expand-modules", serve_expand_modules, 0), + REQ_LINE("ci", serve_ci, RQ_ESSENTIAL), + REQ_LINE("co", serve_co, RQ_ESSENTIAL), + REQ_LINE("update", serve_update, RQ_ESSENTIAL), + REQ_LINE("diff", serve_diff, 0), + REQ_LINE("log", serve_log, 0), + REQ_LINE("rlog", serve_rlog, 0), + REQ_LINE("list", serve_ls, 0), + REQ_LINE("rlist", serve_rls, 0), + /* This allows us to avoid sending `-q' as a command argument to `cvs ls', + * or more accurately, allows us to send `-q' to backwards CVSNT servers. + */ + REQ_LINE("global-list-quiet", serve_noop, RQ_ROOTLESS), + /* Deprecated synonym for rlist, for compatibility with CVSNT. */ + REQ_LINE("ls", serve_rls, 0), + REQ_LINE("add", serve_add, 0), + REQ_LINE("remove", serve_remove, 0), + REQ_LINE("update-patches", serve_ignore, 0), + REQ_LINE("gzip-file-contents", serve_gzip_contents, 0), + REQ_LINE("status", serve_status, 0), + REQ_LINE("rdiff", serve_rdiff, 0), + REQ_LINE("tag", serve_tag, 0), + REQ_LINE("rtag", serve_rtag, 0), + REQ_LINE("import", serve_import, 0), + REQ_LINE("admin", serve_admin, 0), + REQ_LINE("export", serve_export, 0), + REQ_LINE("history", serve_history, 0), + REQ_LINE("release", serve_release, 0), + REQ_LINE("watch-on", serve_watch_on, 0), + REQ_LINE("watch-off", serve_watch_off, 0), + REQ_LINE("watch-add", serve_watch_add, 0), + REQ_LINE("watch-remove", serve_watch_remove, 0), + REQ_LINE("watchers", serve_watchers, 0), + REQ_LINE("editors", serve_editors, 0), + REQ_LINE("init", serve_init, RQ_ROOTLESS), + REQ_LINE("annotate", serve_annotate, 0), + REQ_LINE("rannotate", serve_rannotate, 0), + REQ_LINE("noop", serve_noop, RQ_ROOTLESS), + REQ_LINE("version", serve_version, RQ_ROOTLESS), + REQ_LINE(NULL, NULL, 0) + +#undef REQ_LINE +}; +#endif /* SERVER_SUPPORT or CLIENT_SUPPORT */ + + + +#ifdef SERVER_SUPPORT +static void +serve_valid_requests (char *arg) +{ + struct request *rq; + if (print_pending_error ()) + return; + buf_output0 (buf_to_net, "Valid-requests"); + for (rq = requests; rq->name != NULL; rq++) + { + if (rq->func != NULL) + { + 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 SUNOS_KLUDGE +/* + * Delete temporary files. SIG is the signal making this happen, or + * 0 if not called as a result of a signal. + */ +static int command_pid_is_dead; +static void wait_sig (int sig) +{ + int status; + pid_t r = wait (&status); + if (r == command_pid) + command_pid_is_dead++; +} +#endif /* SUNOS_KLUDGE */ + + + +/* + * This function cleans up after the server. Specifically, it: + * + *
    + *
  1. Sets BUF_TO_NET to blocking and fluxhes it.
  2. + *
  3. With SUNOS_KLUDGE enabled: + *
      + *
    1. Terminates the command process.
    2. + *
    3. Waits on the command process, draining output as necessary.
    4. + *
    + *
  4. + *
  5. Removes the temporary directory.
  6. + *
  7. Flush and shutdown the buffers.
  8. + *
  9. Set ERROR_USE_PROTOCOL and SERVER_ACTIVE to false.
  10. + *
+ * + * NOTES + * This function needs to be reentrant since a call to exit() can cause a + * call to this function, which can then be interrupted by a signal, which + * can cause a second call to this function. + * + * GLOBALS + * buf_from_net The input buffer which brings data from the + * CVS client. + * buf_to_net The output buffer which moves data to the CVS + * client. + * error_use_protocol Set when the server parent process is active. + * Cleared for the server child processes. + * dont_delete_temp Set when a core dump of a child process is + * detected so that the core and related data may + * be preserved. + * Tmpdir The system TMP directory for all temp files. + * noexec Whether we are supposed to change the disk. + * orig_server_temp_dir The temporary directory we created within + * Tmpdir for our duplicate of the client + * workspace. + * + * INPUTS + * None. + * + * ERRORS + * Problems encountered during the cleanup, for instance low memory or + * problems deleting the temp files and directories, can cause the error + * function to be called, which might call exit. If exit gets called in this + * manner. this routine will not complete, but the other exit handlers + * registered via atexit() will still run. + * + * RETURNS + * Nothing. + */ +void +server_cleanup (void) +{ + static short int never_run_again = 0; + + TRACE (TRACE_FUNCTION, "server_cleanup()"); + + /* Since main_cleanup() always calls exit() (via error (1, ...)), we avoid + * allowing this function to be called twice as an optimization. + * + * If we are already in a signal critical section, assume we were called + * via the signal handler and set a flag which will prevent future calls. + * The only time that we should get into one of these functions otherwise + * while still in a critical section is if error(1,...) is called from a + * critical section, in which case we are exiting and interrupts are + * already being ignored. + * + * For Lock_Cleanup(), this is not a run_once variable since Lock_Cleanup() + * can be called to clean up the current lock set multiple times by the + * same run of a CVS command. + * + * For server_cleanup() and rcs_cleanup(), this is not a run_once variable + * since a call to the cleanup function from atexit() could be interrupted + * by the interrupt handler. + */ + if (never_run_again) return; + if (SIG_inCrSect()) never_run_again = 1; + + assert (server_active); + + /* Since we install this function in an atexit() handler before forking, + * reuse the ERROR_USE_PROTOCOL flag, which we know is only set in the + * parent server process, to avoid cleaning up the temp space multiple + * times. Skip the buf_to_net checks too as an optimization since we know + * they will be set to NULL in the child process anyhow. + */ + if (error_use_protocol) + { + if (buf_to_net != NULL) + { + /* Since we're done, go ahead and put BUF_TO_NET back into blocking + * mode and send any pending output. In the usual case there won't + * won't be any, but there might be if an error occured. + */ + + set_block (buf_to_net); + buf_flush (buf_to_net, 1); + + /* Next 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. + */ + + /* Avoid being interrupted during calls which set globals to NULL. + * This avoids having interrupt handlers attempt to use these + * global variables in inconsistent states. + */ + SIG_beginCrSect(); + + if (buf_from_net != NULL) + { + /* Use a tmp var since any of these functions could call exit, + * causing us to be called a second time. + */ + int status; + struct buffer *tmp = buf_from_net; + buf_from_net = NULL; + status = buf_shutdown (tmp); + if (status != 0) + error (0, status, "shutting down buffer from client"); + buf_free (tmp); + } + SIG_endCrSect(); + } + + if (!dont_delete_temp) + { + int save_noexec; + + /* 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 SUNOS_KLUDGE + 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); + /* 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) + 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 (); + } + } + } +#endif /* SUNOS_KLUDGE */ + + /* Make sure our working directory isn't inside the tree we're + going to delete. */ + 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; + + SIG_beginCrSect(); + noexec = 0; + unlink_file_dir (orig_server_temp_dir); + noexec = save_noexec; + SIG_endCrSect(); + } /* !dont_delete_temp && error_use_protocol */ + + SIG_beginCrSect(); + if (buf_to_net != NULL) + { + /* Save BUF_TO_NET and set the global pointer to NULL so that any + * error messages generated during shutdown go to the syslog rather + * than getting lost. + */ + struct buffer *buf_to_net_save = buf_to_net; + buf_to_net = NULL; + + (void) buf_flush (buf_to_net_save, 1); + (void) buf_shutdown (buf_to_net_save); + buf_free (buf_to_net_save); + error_use_protocol = 0; + } + SIG_endCrSect(); + } + + server_active = 0; +} + + + +int server_active = 0; + +int +server (int argc, char **argv) +{ + char *error_prog_name; /* Used in error messages */ + + if (argc == -1) + { + static const char *const msg[] = + { + "Usage: %s %s\n", + " Normally invoked by a cvs client on a remote machine.\n", + NULL + }; + usage (msg); + } + /* Ignore argc and argv. They might be from .cvsrc. */ + + /* + * Set this in .bashrc if you want to give yourself time to attach + * to the subprocess with a debugger. + */ + if (getenv ("CVS_PARENT_SERVER_SLEEP")) + { + int secs = atoi (getenv ("CVS_PARENT_SERVER_SLEEP")); + sleep (secs); + } + + buf_to_net = fd_buffer_initialize (STDOUT_FILENO, 0, + outbuf_memory_error); + buf_from_net = stdio_buffer_initialize (stdin, 0, 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; + + /* OK, now figure out where we stash our temporary files. */ + { + char *p; + + /* 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)) + { + if (alloc_pending (80 + strlen (Tmpdir))) + 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; + int i = 0; + + server_temp_dir = xmalloc (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"); + + 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'; + + /* + * 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"); + + p = server_temp_dir + strlen (server_temp_dir); + sprintf (p, "%ld", (long) getpid ()); + + orig_server_temp_dir = server_temp_dir; + + /* Create the temporary directory, and set the mode to + 700, to discourage random people from tampering with + it. */ + while ((status = mkdir_p (server_temp_dir)) == EEXIST) + { + static const char suffix[] = "abcdefghijklmnopqrstuvwxyz"; + + if (i >= sizeof suffix - 1) break; + if (i == 0) p = server_temp_dir + strlen (server_temp_dir); + p[0] = suffix[i++]; + p[1] = '\0'; + } + if (status != 0) + { + if (alloc_pending (80 + strlen (server_temp_dir))) + sprintf (pending_error_text, + "E can't create temporary directory %s", + server_temp_dir); + pending_error = status; + } +#ifndef CHMOD_BROKEN + else if (chmod (server_temp_dir, S_IRWXU) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (server_temp_dir))) + sprintf (pending_error_text, +"E cannot change permissions on temporary directory %s", + server_temp_dir); + pending_error = save_errno; + } +#endif + else if (CVS_CHDIR (server_temp_dir) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (server_temp_dir))) + sprintf (pending_error_text, +"E cannot change to temporary directory %s", + server_temp_dir); + pending_error = save_errno; + } + } + } + + /* Now initialize our argument vector (for arguments from the client). */ + + /* Small for testing. */ + argument_vector_size = 1; + argument_vector = xmalloc (argument_vector_size * sizeof (char *)); + argument_count = 1; + /* This gets printed if the client supports an option which the + server doesn't, causing the server to print a usage message. + 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. */ + error_prog_name = xmalloc (strlen (program_name) + 8); + sprintf(error_prog_name, "%s server", program_name); + argument_vector[0] = error_prog_name; + + while (1) + { + char *cmd, *orig_cmd; + struct request *rq; + int status; + + status = buf_read_line (buf_from_net, &cmd, NULL); + if (status == -2) + { + 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) + { + int len = strlen (rq->name); + if (cmd[len] == '\0') + cmd += len; + else if (cmd[len] == ' ') + cmd += len + 1; + else + /* + * The first len characters match, but it's a different + * command. e.g. the command is "cooperate" but we matched + * "co". + */ + continue; + + if (!(rq->flags & RQ_ROOTLESS) + && current_parsed_root == NULL) + { + /* For commands which change the way in which data + is sent and received, for example Gzip-stream, + this does the wrong thing. Since the client + assumes that everything is being compressed, + unconditionally, there is no way to give this + error to the client without turning on + compression. The obvious fix would be to make + Gzip-stream RQ_ROOTLESS (with the corresponding + change to the spec), and that might be a good + idea but then again I can see some settings in + CVSROOT about what compression level to allow. + I suppose a more baroque answer would be to + turn on compression (say, at level 1), just + enough to give the "Root request missing" + error. For now we just lose. */ + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Protocol error: Root request missing"); + } + else + (*rq->func) (cmd); + break; + } + if (rq->name == NULL) + { + if (!print_pending_error ()) + { + 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); + } + free (error_prog_name); + + /* We expect the client is done talking to us at this point. If there is + * any data in the buffer or on the network pipe, then something we didn't + * prepare for is happening. + */ + if (!buf_empty (buf_from_net)) + { + /* Try to send the error message to the client, but also syslog it, in + * case the client isn't listening anymore. + */ +#ifdef HAVE_SYSLOG_H + /* FIXME: Can the IP address of the connecting client be retrieved + * and printed here? + */ + syslog (LOG_DAEMON | LOG_ERR, "Dying gasps received from client."); +#endif + error (0, 0, "Dying gasps received from client."); + } + + /* server_cleanup() will be called on a normal exit and close the buffers + * explicitly. + */ + return 0; +} + + + +#if defined (HAVE_KERBEROS) || defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI) +static void +switch_to_user (const char *cvs_username, const char *username) +{ + struct passwd *pw; + + pw = getpwnam (username); + if (pw == NULL) + { + /* check_password contains a similar check, so this usually won't be + reached unless the CVS user is mapped to an invalid system user. */ + + printf ("E Fatal error, aborting.\n\ +error 0 %s: no such system user\n", username); + exit (EXIT_FAILURE); + } + + if (pw->pw_uid == 0) + { +#ifdef HAVE_SYSLOG_H + /* FIXME: Can the IP address of the connecting client be retrieved + * and printed here? + */ + syslog (LOG_DAEMON | LOG_ALERT, + "attempt to root from account: %s", cvs_username + ); +#endif + printf("error 0: root not allowed\n"); + exit (EXIT_FAILURE); + } + +#if HAVE_INITGROUPS + if (initgroups (pw->pw_name, pw->pw_gid) < 0 +# ifdef EPERM + /* At least on the system I tried, initgroups() only works as root. + But we do still want to report ENOMEM and whatever other + errors initgroups() might dish up. */ + && errno != EPERM +# endif + ) + { + /* This could be a warning, but I'm not sure I see the point + in doing that instead of an error given that it would happen + on every connection. We could log it somewhere and not tell + the user. But at least for now make it an error. */ + printf ("error 0 initgroups failed: %s\n", strerror (errno)); + exit (EXIT_FAILURE); + } +#endif /* HAVE_INITGROUPS */ + +#ifdef SETXID_SUPPORT + /* honor the setgid bit iff set*/ + if (getgid() != getegid()) + { + if (setgid (getegid ()) < 0) + { + /* See comments at setuid call below for more discussion. */ + printf ("error 0 setgid failed: %s\n", strerror (errno)); + exit (EXIT_FAILURE); + } + } + else +#endif + { + if (setgid (pw->pw_gid) < 0) + { + /* See comments at setuid call below for more discussion. */ + printf ("error 0 setgid failed: %s\n", strerror (errno)); +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, + "setgid to %d failed (%m): real %d/%d, effective %d/%d ", + pw->pw_gid, getuid(), getgid(), geteuid(), getegid()); +#endif + exit (EXIT_FAILURE); + } + } + + if (setuid (pw->pw_uid) < 0) + { + /* Note that this means that if run as a non-root user, + CVSROOT/passwd must contain the user we are running as + (e.g. "joe:FsEfVcu:cvs" if run as "cvs" user). This seems + cleaner than ignoring the error like CVS 1.10 and older but + it does mean that some people might need to update their + CVSROOT/passwd file. */ + printf ("error 0 setuid failed: %s\n", strerror (errno)); +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, + "setuid to %d failed (%m): real %d/%d, effective %d/%d ", + pw->pw_uid, getuid(), getgid(), geteuid(), getegid()); +#endif + exit (EXIT_FAILURE); + } + + /* 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); + +#ifdef AUTH_SERVER_SUPPORT + /* Make sure our CVS_Username has been set. */ + if (CVS_Username == NULL) + CVS_Username = xstrdup (username); +#endif + +#if HAVE_PUTENV + /* Set LOGNAME, USER and CVS_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); + +#ifdef AUTH_SERVER_SUPPORT + env = xmalloc (sizeof "CVS_USER=" + strlen (CVS_Username)); + (void) sprintf (env, "CVS_USER=%s", CVS_Username); + (void) putenv (env); +#endif + } +#endif /* HAVE_PUTENV */ +} +#endif + +#ifdef AUTH_SERVER_SUPPORT + +extern char *crypt (const char *, const char *); + + +/* + * 0 means no entry found for this user. + * 1 means entry found and password matches (or found password is empty) + * 2 means entry found, but password does not match. + * + * If 1, 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). + * kff todo: FIXME: last sentence is not true, it applies to caller. + */ +static int +check_repository_password (char *username, char *password, char *repository, char **host_user_ptr) +{ + int retval = 0; + FILE *fp; + char *filename; + char *linebuf = NULL; + size_t linebuf_len; + int found_it = 0; + int namelen; + + /* We don't use current_parsed_root->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 (CVSROOTADM) + + 1 + + strlen (CVSROOTADM_PASSWD) + + 1); + + (void) sprintf (filename, "%s/%s/%s", repository, + CVSROOTADM, CVSROOTADM_PASSWD); + + fp = CVS_FOPEN (filename, "r"); + if (fp == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot open %s", filename); + return 0; + } + + /* Look for a relevant line -- one with this user's name. */ + namelen = strlen (username); + while (getline (&linebuf, &linebuf_len, fp) >= 0) + { + if ((strncmp (linebuf, username, namelen) == 0) + && (linebuf[namelen] == ':')) + { + found_it = 1; + break; + } + } + if (ferror (fp)) + error (0, errno, "cannot read %s", filename); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", filename); + + /* If found_it, then linebuf contains the information we need. */ + if (found_it) + { + char *found_password, *host_user_tmp; + char *non_cvsuser_portion; + + /* We need to make sure lines such as + * + * "username::sysuser\n" + * "username:\n" + * "username: \n" + * + * all result in a found_password of NULL, but we also need to + * make sure that + * + * "username: :sysuser\n" + * "username: :sysuser\n" + * + * continues to result in an impossible password. That way, + * an admin would be on safe ground by going in and tacking a + * space onto the front of a password to disable the account + * (a technique some people use to close accounts + * temporarily). + */ + + /* Make `non_cvsuser_portion' contain everything after the CVS + username, but null out any final newline. */ + non_cvsuser_portion = linebuf + namelen; + strtok (non_cvsuser_portion, "\n"); + + /* If there's a colon now, we just want to inch past it. */ + if (strchr (non_cvsuser_portion, ':') == non_cvsuser_portion) + non_cvsuser_portion++; + + /* Okay, after this conditional chain, found_password and + host_user_tmp will have useful values: */ + + if ((non_cvsuser_portion == NULL) + || (strlen (non_cvsuser_portion) == 0) + || ((strspn (non_cvsuser_portion, " \t")) + == strlen (non_cvsuser_portion))) + { + found_password = NULL; + host_user_tmp = NULL; + } + else if (strncmp (non_cvsuser_portion, ":", 1) == 0) + { + found_password = NULL; + host_user_tmp = non_cvsuser_portion + 1; + if (strlen (host_user_tmp) == 0) + host_user_tmp = NULL; + } + else + { + found_password = strtok (non_cvsuser_portion, ":"); + host_user_tmp = strtok (NULL, ":"); + } + + /* Of course, maybe there was no system user portion... */ + if (host_user_tmp == NULL) + host_user_tmp = username; + + /* Verify blank passwords directly, otherwise use crypt(). */ + if ((found_password == NULL) + || ((strcmp (found_password, crypt (password, found_password)) + == 0))) + { + /* Give host_user_ptr permanent storage. */ + *host_user_ptr = xstrdup (host_user_tmp); + retval = 1; + } + else + { +#ifdef LOG_AUTHPRIV + syslog (LOG_AUTHPRIV | LOG_NOTICE, + "password mismatch for %s in %s: %s vs. %s", username, + repository, crypt(password, found_password), found_password); +#endif + *host_user_ptr = NULL; + retval = 2; + } + } + else /* Didn't find this user, so deny access. */ + { + *host_user_ptr = NULL; + retval = 0; + } + + free (filename); + if (linebuf) + free (linebuf); + + return retval; +} + +#ifdef HAVE_PAM + +# include + +struct cvs_pam_userinfo { + char *username; + char *password; +}; + +static int +cvs_pam_conv (int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + int i; + struct pam_response *response; + struct cvs_pam_userinfo *ui = (struct cvs_pam_userinfo *)appdata_ptr; + + assert (ui && ui->username && ui->password && msg && resp); + + response = xmalloc(num_msg * sizeof (struct pam_response)); + memset(response, 0, num_msg * sizeof (struct pam_response)); + + for (i = 0; i < num_msg; i++) + { + switch (msg[i]->msg_style) + { + /* PAM wants a username */ + case PAM_PROMPT_ECHO_ON: + response[i].resp = xstrdup (ui->username); + break; + /* PAM wants a password */ + case PAM_PROMPT_ECHO_OFF: + response[i].resp = xstrdup (ui->password); + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + printf ("E %s\n", msg[i]->msg); + break; + /* PAM wants something we don't understand - bail out */ + default: + goto cleanup; + } + } + + *resp = response; + return PAM_SUCCESS; + +cleanup: + for (i = 0; i < num_msg; i++) + { + if (response[i].resp) + { + free (response[i].resp); + response[i].resp = 0; + } + } + free (response); + return PAM_CONV_ERR; +} + + + +static int +check_system_password (char *username, char *password) +{ + pam_handle_t *pamh = NULL; + int retval, err; + struct cvs_pam_userinfo ui = { username, password }; + struct pam_conv conv = { cvs_pam_conv, (void *)&ui }; + char *pam_stage = "start"; + + retval = pam_start (PAM_SERVICE_NAME, username, &conv, &pamh); + + if (retval == PAM_SUCCESS) { + pam_stage = "authenticate"; + retval = pam_authenticate(pamh, 0); + } + + if (retval == PAM_SUCCESS) { + pam_stage = "account"; + retval = pam_acct_mgmt (pamh, 0); + } + + if (retval != PAM_SUCCESS) + printf ("E PAM %s error: %s\n", pam_stage, pam_strerror(pamh, retval)); + + if ((err = pam_end (pamh, retval)) != PAM_SUCCESS) + { + printf ("E Fatal error, aborting.\n" + "pam failed to release authenticator\n" + "PAM error %s\n", pam_strerror (NULL, err)); + exit (EXIT_FAILURE); + } + + return retval == PAM_SUCCESS; /* indicate success */ +} +#else +static int +check_system_password (char *username, char *password) +{ + char *found_passwd = NULL; + struct passwd *pw; +#ifdef HAVE_GETSPNAM + { + struct spwd *spw; + + spw = getspnam (username); + if (spw != NULL) + found_passwd = spw->sp_pwdp; + } +#endif + + if (found_passwd == NULL && (pw = getpwnam (username)) != NULL) + found_passwd = pw->pw_passwd; + + if (found_passwd == NULL) + { + printf ("E Fatal error, aborting.\n\ +error 0 %s: no such user\n", username); + + exit (EXIT_FAILURE); + } + + /* Allow for dain bramaged HPUX passwd aging + * - Basically, HPUX adds a comma and some data + * about whether the passwd has expired or not + * on the end of the passwd field. + * - This code replaces the ',' with '\0'. + * + * FIXME - our workaround is brain damaged too. I'm + * guessing that HPUX WANTED other systems to think the + * password was wrong so logins would fail if the + * system didn't handle expired passwds and the passwd + * might be expired. I think the way to go here + * is with PAM. + */ + strtok (found_passwd, ","); + + if (*found_passwd) + { + /* user exists and has a password */ + if (strcmp (found_passwd, crypt (password, found_passwd)) == 0) + return 1; + else + { +#ifdef LOG_AUTHPRIV + syslog (LOG_AUTHPRIV | LOG_NOTICE, + "password mismatch for %s: %s vs. %s", username, + crypt(password, found_passwd), found_passwd); +#endif + return 0; + } + } + +#ifdef LOG_AUTHPRIV + syslog (LOG_AUTHPRIV | LOG_NOTICE, + "user %s authenticated because of blank system password", + username); +#endif + return 1; +} +#endif + + + +/* Return a hosting username if password matches, else NULL. */ +static char * +check_password (char *username, char *password, char *repository) +{ + int rc; + 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 + not, we'll check /etc/passwd or maybe whatever is configured via PAM. */ + + rc = check_repository_password (username, password, repository, + &host_user); + + if (rc == 2) + return NULL; + + if (rc == 1) + /* host_user already set by reference, so just return. */ + goto handle_return; + + assert (rc == 0); + + if (!system_auth) + { + /* Note that the message _does_ distinguish between the case in + which we check for a system password and the case in which + we do not. It is a real pain to track down why it isn't + letting you in if it won't say why, and I am not convinced + that the potential information disclosure to an attacker + outweighs this. */ + printf ("error 0 no such user %s in CVSROOT/passwd\n", username); + + exit (EXIT_FAILURE); + } + + /* No cvs password found, so try /etc/passwd. */ + if ( check_system_password(username, password) ) + host_user = xstrdup (username); + else + host_user = NULL; + +#ifdef LOG_AUTHPRIV + if (!host_user) + syslog (LOG_AUTHPRIV | LOG_NOTICE, + "login refused for %s: user has no password", username); +#endif + +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; +} + +#endif /* AUTH_SERVER_SUPPORT */ + +#if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI) + +/* 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 +pserver_authenticate_connection (void) +{ + char *tmp = NULL; + size_t tmp_allocated = 0; +#ifdef AUTH_SERVER_SUPPORT + char *repository = NULL; + size_t repository_allocated = 0; + char *username = NULL; + size_t username_allocated = 0; + char *password = NULL; + size_t password_allocated = 0; + + char *host_user; + char *descrambled_password; +#endif /* AUTH_SERVER_SUPPORT */ + int verify_and_exit = 0; + + /* The Authentication Protocol. Client sends: + * + * BEGIN AUTH REQUEST\n + * \n + * \n + * \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; + + if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof on) < 0) + { +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, "error setting KEEPALIVE: %m"); +#endif + } + } +#endif + + /* Make sure the protocol starts off on the right foot... */ + if( getnline( &tmp, &tmp_allocated, PATH_MAX, stdin ) < 0 ) + { +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_NOTICE, "bad auth protocol start: EOF"); +#endif + error (1, 0, "bad auth protocol start: EOF"); + } + + if (strcmp (tmp, "BEGIN VERIFICATION REQUEST\n") == 0) + verify_and_exit = 1; + else if (strcmp (tmp, "BEGIN AUTH REQUEST\n") == 0) + ; + else if (strcmp (tmp, "BEGIN GSSAPI REQUEST\n") == 0) + { +#ifdef HAVE_GSSAPI + free (tmp); + gserver_authenticate_connection (); + return; +#else + error (1, 0, "GSSAPI authentication not supported by this server"); +#endif + } + else + error (1, 0, "bad auth protocol start: %s", tmp); + +#ifndef AUTH_SERVER_SUPPORT + + error (1, 0, "Password authentication not supported by this server"); + +#else /* AUTH_SERVER_SUPPORT */ + + /* Get the three important pieces of information in order. */ + /* See above comment about error handling. */ + getnline( &repository, &repository_allocated, PATH_MAX, stdin ); + getnline( &username, &username_allocated, PATH_MAX, stdin ); + getnline( &password, &password_allocated, PATH_MAX, stdin ); + + /* Make them pure. + * + * We check that none of the lines were truncated by getnline in order + * to be sure that we don't accidentally allow a blind DOS attack to + * authenticate, however slim the odds of that might be. + */ + if (!strip_trailing_newlines (repository) + || !strip_trailing_newlines (username) + || !strip_trailing_newlines (password)) + error (1, 0, "Maximum line length exceeded during authentication."); + + /* ... and make sure the protocol ends on the right foot. */ + /* See above comment about error handling. */ + getnline( &tmp, &tmp_allocated, 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); + } + if (!root_allow_ok (repository)) + { + printf ("error 0 %s: no such repository\n", repository); +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_NOTICE, "login refused for %s", repository); +#endif + goto i_hate_you; + } + + /* OK, now parse the config file, so we can use it to control how + to check passwords. If there was an error parsing the config + file, parse_config already printed an error. We keep going. + Why? Because if we didn't, then there would be no way to check + in a new CVSROOT/config file to fix the broken one! */ + parse_config (repository); + + /* We need the real cleartext before we hash it. */ + descrambled_password = descramble (password); + host_user = check_password (username, descrambled_password, repository); + if (host_user == NULL) + { +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_NOTICE, "login failure (for %s)", repository); +#endif + memset (descrambled_password, 0, strlen (descrambled_password)); + free (descrambled_password); + i_hate_you: + printf ("I HATE YOU\n"); + fflush (stdout); + + /* Don't worry about server_cleanup, server_active isn't set + yet. */ + exit (EXIT_FAILURE); + } + memset (descrambled_password, 0, strlen (descrambled_password)); + free (descrambled_password); + + /* Don't go any farther if we're just responding to "cvs login". */ + if (verify_and_exit) + { + printf ("I LOVE YOU\n"); + fflush (stdout); + 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 (username, host_user); + free (host_user); + free (tmp); + free (repository); + free (username); + free (password); + + printf ("I LOVE YOU\n"); + fflush (stdout); +#endif /* AUTH_SERVER_SUPPORT */ +} + +#endif /* AUTH_SERVER_SUPPORT || HAVE_GSSAPI */ + + +#ifdef HAVE_KERBEROS +void +kserver_authenticate_connection( void ) +{ + 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)); + + 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; + + if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof on) < 0) + { +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, "error setting KEEPALIVE: %m"); +#endif + } + } +#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)); + + 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)); + + exit (EXIT_FAILURE); + } + + /* Switch to run as this user. */ + switch_to_user ("Kerberos 4", user); +} +#endif /* HAVE_KERBEROS */ + +#ifdef HAVE_GSSAPI + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN (256) +#endif + +/* Authenticate a GSSAPI connection. This is called from + pserver_authenticate_connection, and it handles success and failure + the same way. */ + +static void +gserver_authenticate_connection (void) +{ + char hostname[MAXHOSTNAMELEN]; + struct hostent *hp; + gss_buffer_desc tok_in, tok_out; + char buf[1024]; + char *credbuf; + size_t credbuflen; + OM_uint32 stat_min, ret; + gss_name_t server_name, client_name; + gss_cred_id_t server_creds; + int nbytes; + gss_OID mechid; + + gethostname (hostname, sizeof hostname); + hp = gethostbyname (hostname); + if (hp == NULL) + error (1, 0, "can't get canonical hostname"); + + sprintf (buf, "cvs@%s", hp->h_name); + tok_in.value = buf; + tok_in.length = strlen (buf); + + if (gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE, + &server_name) != GSS_S_COMPLETE) + error (1, 0, "could not import GSSAPI service name %s", buf); + + /* Acquire the server credential to verify the client's + authentication. */ + if (gss_acquire_cred (&stat_min, server_name, 0, GSS_C_NULL_OID_SET, + GSS_C_ACCEPT, &server_creds, + NULL, NULL) != GSS_S_COMPLETE) + error (1, 0, "could not acquire GSSAPI server credentials"); + + gss_release_name (&stat_min, &server_name); + + /* The client will send us a two byte length followed by that many + bytes. */ + if (fread (buf, 1, 2, stdin) != 2) + error (1, errno, "read of length failed"); + + nbytes = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff); + if (nbytes <= sizeof buf) + { + credbuf = buf; + credbuflen = sizeof buf; + } + else + { + credbuflen = nbytes; + credbuf = xmalloc (credbuflen); + } + + if (fread (credbuf, 1, nbytes, stdin) != nbytes) + error (1, errno, "read of data failed"); + + gcontext = GSS_C_NO_CONTEXT; + tok_in.length = nbytes; + tok_in.value = credbuf; + + if (gss_accept_sec_context (&stat_min, + &gcontext, /* context_handle */ + server_creds, /* verifier_cred_handle */ + &tok_in, /* input_token */ + NULL, /* channel bindings */ + &client_name, /* src_name */ + &mechid, /* mech_type */ + &tok_out, /* output_token */ + &ret, + NULL, /* ignore time_rec */ + NULL) /* ignore del_cred_handle */ + != GSS_S_COMPLETE) + { + error (1, 0, "could not verify credentials"); + } + + /* FIXME: Use Kerberos v5 specific code to authenticate to a user. + We could instead use an authentication to access mapping. */ + { + krb5_context kc; + krb5_principal p; + gss_buffer_desc desc; + + krb5_init_context (&kc); + if (gss_display_name (&stat_min, client_name, &desc, + &mechid) != GSS_S_COMPLETE + || krb5_parse_name (kc, ((gss_buffer_t) &desc)->value, &p) != 0 + || krb5_aname_to_localname (kc, p, sizeof buf, buf) != 0 + || krb5_kuserok (kc, p, buf) != TRUE) + { + error (1, 0, "access denied"); + } + krb5_free_principal (kc, p); + krb5_free_context (kc); + } + + if (tok_out.length != 0) + { + char cbuf[2]; + + cbuf[0] = (tok_out.length >> 8) & 0xff; + cbuf[1] = tok_out.length & 0xff; + if (fwrite (cbuf, 1, 2, stdout) != 2 + || (fwrite (tok_out.value, 1, tok_out.length, stdout) + != tok_out.length)) + error (1, errno, "fwrite failed"); + } + + switch_to_user ("GSSAPI", buf); + + if (credbuf != buf) + free (credbuf); + + printf ("I LOVE YOU\n"); + fflush (stdout); +} + +#endif /* HAVE_GSSAPI */ + +#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; + +/* This global variable is non-zero if the users requests stream + authentication on the command line. */ +int cvsauthenticate; + +#ifdef ENCRYPTION + +#ifdef HAVE_KERBEROS + +/* An encryption interface using Kerberos. This is built on top of a + packetizing buffer. */ + +/* This structure is the closure field of the Kerberos translation + routines. */ +struct krb_encrypt_data +{ + /* The Kerberos key schedule. */ + Key_schedule sched; + /* The Kerberos DES block. */ + C_Block block; +}; + + + +/* Decrypt Kerberos data. */ +static int +krb_encrypt_input( void *fnclosure, const char *input, char *output, int size ) +{ + struct krb_encrypt_data *kd = (struct krb_encrypt_data *) fnclosure; + int tcount; + + des_cbc_encrypt ((C_Block *) input, (C_Block *) output, + size, kd->sched, &kd->block, 0); + + /* SIZE is the size of the buffer, which is set by the encryption + routine. The packetizing buffer will arrange for the first two + bytes in the decrypted buffer to be the real (unaligned) + length. As a safety check, make sure that the length in the + buffer corresponds to SIZE. Note that the length in the buffer + is just the length of the data. We must add 2 to account for + the buffer count itself. */ + tcount = ((output[0] & 0xff) << 8) + (output[1] & 0xff); + if (((tcount + 2 + 7) & ~7) != size) + error (1, 0, "Decryption failure"); + + return 0; +} + + + +/* Encrypt Kerberos data. */ +static int +krb_encrypt_output( void *fnclosure, const char *input, char *output, + int size, int *translated ) +{ + struct krb_encrypt_data *kd = (struct krb_encrypt_data *) fnclosure; + int aligned; + + /* 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 SIZE to an 8 byte boundary. Note that SIZE includes the + two byte buffer count at the start of INPUT which was added by + the packetizing buffer. */ + aligned = (size + 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 *) input, (C_Block *) output, aligned, + kd->sched, &kd->block, 1); + + *translated = aligned; + + return 0; +} + + + +/* Create a Kerberos encryption buffer. We use a packetizing buffer + with Kerberos encryption translation routines. */ +struct buffer * +krb_encrypt_buffer_initialize( struct buffer *buf, int input, + Key_schedule sched, C_Block block, + void *memory( struct buffer * ) ) +{ + struct krb_encrypt_data *kd; + + kd = (struct krb_encrypt_data *) xmalloc (sizeof *kd); + memcpy (kd->sched, sched, sizeof (Key_schedule)); + memcpy (kd->block, block, sizeof (C_Block)); + + return packetizing_buffer_initialize (buf, + input ? krb_encrypt_input : NULL, + input ? NULL : krb_encrypt_output, + kd, + memory); +} + +#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. */ +void +cvs_output (const char *str, size_t len) +{ + if (len == 0) + len = strlen (str); +#ifdef SERVER_SUPPORT + if (error_use_protocol) + { + if (buf_to_net) + { + buf_output (saved_output, str, len); + buf_copy_lines (buf_to_net, saved_output, 'M'); + } +# if HAVE_SYSLOG_H + else + syslog (LOG_DAEMON | LOG_ERR, + "Attempt to write message after close of network buffer. " + "Message was: %s", + str); +# endif /* HAVE_SYSLOG */ + } + else if (server_active) + { + if (protocol) + { + buf_output (saved_output, str, len); + buf_copy_lines (protocol, saved_output, 'M'); + buf_send_counted (protocol); + } +# if HAVE_SYSLOG_H + else + syslog (LOG_DAEMON | LOG_ERR, + "Attempt to write message before initialization of " + "protocol buffer. Message was: %s", + str); +# endif /* HAVE_SYSLOG */ + } + else +#endif + { + size_t written; + size_t to_write = len; + const char *p = str; + + /* For symmetry with cvs_outerr we would call fflush (stderr) + here. I guess the assumption is that stderr will be + unbuffered, so we don't need to. That sounds like a sound + assumption from the manpage I looked at, but if there was + something fishy about it, my guess is that calling fflush + would not produce a significant performance problem. */ + + while (to_write > 0) + { + written = fwrite (p, 1, to_write, stdout); + if (written == 0) + break; + p += written; + to_write -= written; + } + } +} + +/* Output LEN bytes at STR in binary mode. If LEN is zero, then + output zero bytes. */ + +void +cvs_output_binary (char *str, size_t len) +{ +#ifdef SERVER_SUPPORT + if (error_use_protocol || server_active) + { + struct buffer *buf; + char size_text[40]; + + if (error_use_protocol) + buf = buf_to_net; + else + buf = protocol; + + assert (buf); + + if (!supported_response ("Mbinary")) + { + error (0, 0, "\ +this client does not support writing binary files to stdout"); + return; + } + + buf_output0 (buf, "Mbinary\012"); + sprintf (size_text, "%lu\012", (unsigned long) len); + buf_output0 (buf, size_text); + + /* Not sure what would be involved in using buf_append_data here + without stepping on the toes of our caller (which is responsible + for the memory allocation of STR). */ + buf_output (buf, str, len); + + if (!error_use_protocol) + buf_send_counted (protocol); + } + else +#endif + { + size_t written; + size_t to_write = len; + const char *p = str; + + /* For symmetry with cvs_outerr we would call fflush (stderr) + here. I guess the assumption is that stderr will be + unbuffered, so we don't need to. That sounds like a sound + assumption from the manpage I looked at, but if there was + something fishy about it, my guess is that calling fflush + would not produce a significant performance problem. */ +#ifdef USE_SETMODE_STDOUT + int oldmode; + + /* It is possible that this should be the same ifdef as + USE_SETMODE_BINARY but at least for the moment we keep them + separate. Mostly this is just laziness and/or a question + of what has been tested where. Also there might be an + issue of setmode vs. _setmode. */ + /* The Windows doc says to call setmode only right after startup. + I assume that what they are talking about can also be helped + by flushing the stream before changing the mode. */ + fflush (stdout); + oldmode = _setmode (_fileno (stdout), OPEN_BINARY); + if (oldmode < 0) + error (0, errno, "failed to setmode on stdout"); +#endif + + while (to_write > 0) + { + written = fwrite (p, 1, to_write, stdout); + if (written == 0) + break; + p += written; + to_write -= written; + } +#ifdef USE_SETMODE_STDOUT + fflush (stdout); + if (_setmode (_fileno (stdout), oldmode) != OPEN_BINARY) + error (0, errno, "failed to setmode on stdout"); +#endif + } +} + + + +/* Like CVS_OUTPUT but output is for stderr not stdout. */ +void +cvs_outerr (const char *str, size_t len) +{ + if (len == 0) + len = strlen (str); +#ifdef SERVER_SUPPORT + if (error_use_protocol) + { + if (buf_to_net) + { + buf_output (saved_outerr, str, len); + buf_copy_lines (buf_to_net, saved_outerr, 'E'); + } +# if HAVE_SYSLOG_H + else + syslog (LOG_DAEMON | LOG_ERR, + "Attempt to write error message after close of network " + "buffer. Message was: %s", + str); +# endif /* HAVE_SYSLOG */ + } + else if (server_active) + { + if (protocol) + { + buf_output (saved_outerr, str, len); + buf_copy_lines (protocol, saved_outerr, 'E'); + buf_send_counted (protocol); + } +# if HAVE_SYSLOG_H + else + syslog (LOG_DAEMON | LOG_ERR, + "Attempt to write error message before initialization of " + "protocol buffer. Message was: %s", + str); +# endif /* HAVE_SYSLOG */ + } + else +#endif + { + size_t written; + size_t to_write = len; + 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 (p, 1, to_write, stderr); + if (written == 0) + break; + p += written; + to_write -= written; + } + } +} + + + +/* 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 (void) +{ +#ifdef SERVER_SUPPORT + if (error_use_protocol) + { + /* skip the actual stderr flush in this case since the parent process + * on the server should only be writing to stdout anyhow + */ + /* Flush what we can to the network, but don't block. */ + buf_flush (buf_to_net, 0); + } + else if (server_active) + { + /* make sure stderr is flushed before we send the flush count on the + * protocol pipe + */ + fflush (stderr); + /* Send a special count to tell the parent to flush. */ + buf_send_special_count (protocol, -2); + } + 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 (void) +{ +#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. + + FIXME - DRP - I tried to implement this and triggered the following + error: "Protocol error: uncounted data discarded". I don't need + this feature right now, so I'm not going to bother with it yet. + */ + buf_send_special_count (protocol, -1); + } + else +#endif + fflush (stdout); +} + + + +/* Output TEXT, tagging it according to TAG. There are lots more + details about what TAG means in cvsclient.texi but for the simple + case (e.g. non-client/server), TAG is just "newline" to output a + newline (in which case TEXT must be NULL), and any other tag to + output normal text. + + Note that there is no way to output either \0 or \n as part of TEXT. */ + +void +cvs_output_tagged (const char *tag, const char *text) +{ + if (text != NULL && strchr (text, '\n') != NULL) + /* Uh oh. The protocol has no way to cope with this. For now + we dump core, although that really isn't such a nice + response given that this probably can be caused by newlines + in filenames and other causes other than bugs in CVS. Note + that we don't want to turn this into "MT newline" because + this case is a newline within a tagged item, not a newline + as extraneous sugar for the user. */ + assert (0); + + /* Start and end tags don't take any text, per cvsclient.texi. */ + if (tag[0] == '+' || tag[0] == '-') + assert (text == NULL); + +#ifdef SERVER_SUPPORT + if (server_active && supported_response ("MT")) + { + struct buffer *buf; + + if (error_use_protocol) + buf = buf_to_net; + else + buf = protocol; + + buf_output0 (buf, "MT "); + buf_output0 (buf, tag); + if (text != NULL) + { + buf_output (buf, " ", 1); + buf_output0 (buf, text); + } + buf_output (buf, "\n", 1); + + if (!error_use_protocol) + buf_send_counted (protocol); + } + else +#endif /* SERVER_SUPPORT */ + { + /* No MT support or we are using a local repository. */ + if (strcmp (tag, "newline") == 0) + cvs_output ("\n", 1); + else if (strcmp (tag, "date") == 0) + { +#ifdef SERVER_SUPPORT + if (server_active) + /* Output UTC when running as a server without MT support in + * the client since it is likely to be more meaningful than + * localtime. + */ + cvs_output (text, 0); + else +#endif /* SERVER_SUPPORT */ + { + char *date_in = xstrdup (text); + char *date = format_date_alloc (date_in); + cvs_output (date, 0); + free (date); + free (date_in); + } + } + else if (text != NULL) + cvs_output (text, 0); + } +} diff --git a/contrib/cvs-1.12.9/src/server.h b/contrib/cvs-1.12.9/src/server.h new file mode 100644 index 0000000000..aca0c61cc8 --- /dev/null +++ b/contrib/cvs-1.12.9/src/server.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2003 The Free Software Foundation. + * + * Portions Copyright (c) 2003 Derek Price + * and Ximbiot , + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS kit. + * + * + * + * This file contains the interface between the server and the rest of CVS. + */ + +/* Miscellaneous stuff which isn't actually particularly server-specific. */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + + +/* + * Expand to `S', ` ', or the empty string. Used in `%s-> ...' trace printfs. + */ +#ifdef SERVER_SUPPORT +# define CLIENT_SERVER_STR ((server_active) ? "S" : " ") +#else +# define CLIENT_SERVER_STR "" +#endif + +#ifdef SERVER_SUPPORT + +/* + * Nonzero if we are using the server. Used by various places to call + * server-specific functions. + */ +extern int server_active; + +/* Server functions exported to the rest of CVS. */ + +/* Run the server. */ +int server (int argc, char **argv); + +/* kserver user authentication. */ +# ifdef HAVE_KERBEROS +void kserver_authenticate_connection (void); +# endif + +/* pserver user authentication. */ +# if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI) +void pserver_authenticate_connection (void); +# endif + +/* See server.c for description. */ +void server_pathname_check (char *); + +/* We have a new Entries line for a file. TAG or DATE can be NULL. */ +void server_register (const char *name, const char *version, + const char *timestamp, const char *options, + const char *tag, const char *date, const char *conflict); + +/* Set the modification time of the next file sent. This must be + followed by a call to server_updated on the same file. */ +void server_modtime (struct file_info *finfo, Vers_TS *vers_ts); + +/* + * We want to nuke the Entries line for a file, and (unless + * server_scratch_entry_only is subsequently called) the file itself. + */ +void server_scratch (const char *name); + +/* + * The file which just had server_scratch called on it needs to have only + * the Entries line removed, not the file itself. + */ +void server_scratch_entry_only (void); + +/* + * We just successfully checked in FILE (which is just the bare + * filename, with no directory). REPOSITORY is the directory for the + * repository. + */ +void server_checked_in (const char *file, const char *update_dir, + const char *repository); + +void server_copy_file (const char *file, const char *update_dir, + const char *repository, const char *newfile); + +/* Send the appropriate responses for a file described by FINFO and + VERS. 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). MODE is the mode the + file should get, or (mode_t) -1 if this should be obtained from the + file itself. CHECKSUM is the MD5 checksum of the file, or NULL if + this need not be sent. If FILEBUF is not NULL, it holds the + contents of the file, in which case the file itself may not exist. + If FILEBUF is not NULL, server_updated will free it. */ +enum server_updated_arg4 +{ + SERVER_UPDATED, + SERVER_MERGED, + SERVER_PATCHED, + SERVER_RCS_DIFF +}; + +struct buffer; + +void server_updated (struct file_info *finfo, Vers_TS *vers, + enum server_updated_arg4 updated, mode_t mode, + unsigned char *checksum, struct buffer *filebuf); + +/* Whether we should send RCS format patches. */ +int server_use_rcs_diff (void); + +/* Set the Entries.Static flag. */ +void server_set_entstat (const char *update_dir, const char *repository); +/* Clear it. */ +void server_clear_entstat (const char *update_dir, const char *repository); + +/* Set or clear a per-directory sticky tag or date. */ +void server_set_sticky (const char *update_dir, const char *repository, + const char *tag, const char *date, int nonbranch); + +/* Send Clear-template response. */ +void server_clear_template (const char *update_dir, const char *repository); + +/* Send Template response. */ +void server_template (const char *update_dir, const char *repository); + +void server_update_entries (const char *file, const char *update_dir, + const char *repository, + 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; + +void server_cleanup (void); + +#ifdef SERVER_FLOWCONTROL +/* Pause if it's convenient to avoid memory blowout */ +void server_pause_check (void); +#endif /* SERVER_FLOWCONTROL */ + +#ifdef AUTH_SERVER_SUPPORT +extern char *CVS_Username; +extern int system_auth; +#endif /* AUTH_SERVER_SUPPORT */ + +#endif /* SERVER_SUPPORT */ + +/* Stuff shared with the client. */ +struct request +{ + /* Name of the request. */ + char *name; + +#ifdef SERVER_SUPPORT + /* + * Function to carry out the request. ARGS is the text of the command + * after name and, if present, a single space, have been stripped off. + */ + void (*func) (char *args); +#endif + + /* One or more of the RQ_* flags described below. */ + int flags; + + /* If set, failure to implement this request can imply a fatal + error. This should be set only for commands which were in the + original version of the protocol; it should not be set for new + commands. */ +#define RQ_ESSENTIAL 1 + + /* Set by the client if the server we are talking to supports it. */ +#define RQ_SUPPORTED 2 + + /* If set, and client and server both support the request, the + client should tell the server by making the request. */ +#define RQ_ENABLEME 4 + + /* The server may accept this request before "Root". */ +#define RQ_ROOTLESS 8 +}; + +/* Table of requests ending with an entry with a NULL name. */ +extern struct request requests[]; + +/* Gzip library, see zlib.c. */ +int gunzip_and_write (int, char *, unsigned char *, size_t); +int read_and_gzip (int, const char *, unsigned char **, size_t *, size_t *, + int); diff --git a/contrib/cvs-1.12.9/src/socket-client.c b/contrib/cvs-1.12.9/src/socket-client.c new file mode 100644 index 0000000000..20426330d8 --- /dev/null +++ b/contrib/cvs-1.12.9/src/socket-client.c @@ -0,0 +1,259 @@ +/* CVS socket client stuff. + + 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 + +#include "cvs.h" +#include "buffer.h" + +#ifdef CLIENT_SUPPORT + +#include "socket-client.h" + + +#if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI) + +struct hostent * +init_sockaddr( struct sockaddr_in *name, 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 (shortport); + hostinfo = gethostbyname (hostname); + if (hostinfo == NULL) + { + fprintf (stderr, "Unknown host %s.\n", hostname); + exit (EXIT_FAILURE); + } + name->sin_addr = *(struct in_addr *) hostinfo->h_addr; + return hostinfo; +} + +#endif + +#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. + + 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. */ + +/* 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. */ + +/* 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). */ + +/* We use an instance of this structure as the closure field. */ + +struct socket_buffer +{ + /* The socket number. */ + int socket; +}; + +static int socket_buffer_input (void *, char *, int, int, int *); +static int socket_buffer_output (void *, const char *, int, int *); +static int socket_buffer_flush (void *); +static int socket_buffer_shutdown (struct buffer *); + + + +/* Create a buffer based on a socket. */ + +struct buffer * +socket_buffer_initialize( int socket, int input, + void (*memory) ( struct buffer * ) ) +{ + struct socket_buffer *sbuf = xmalloc (sizeof *sbuf); + sbuf->socket = socket; + return buf_initialize (input ? socket_buffer_input : NULL, + input ? NULL : socket_buffer_output, + input ? NULL : socket_buffer_flush, + NULL, + socket_buffer_shutdown, + memory, + sbuf); +} + + + +/* The buffer input function for a buffer built on a socket. */ + +static int +socket_buffer_input( void *closure, char *data, int need, int size, int *got ) +{ + struct socket_buffer *sb = (struct socket_buffer *) closure; + int nbytes; + + /* 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. */ + + *got = 0; + + do + { + + /* Note that for certain (broken?) networking stacks, like + VMS's UCX (not sure what version, problem reported with + recv() in 1997), and (according to windows-NT/config.h) + Windows NT 3.51, we must call recv or send with a + moderately sized buffer (say, less than 200K or something), + or else there may be network errors (somewhat hard to + produce, e.g. WAN not LAN or some such). buf_read_data + makes sure that we only recv() BUFFER_DATA_SIZE bytes at + a time. */ + + nbytes = recv (sb->socket, data, size, 0); + if (nbytes < 0) + error (1, 0, "reading from server: %s", SOCK_STRERROR (SOCK_ERRNO)); + if (nbytes == 0) + { + /* 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); + + return 0; +} + + + +/* The buffer output function for a buffer built on a socket. */ + +static int +socket_buffer_output( void *closure, const char *data, int have, int *wrote ) +{ + struct socket_buffer *sb = (struct socket_buffer *) closure; + + *wrote = have; + + /* See comment in socket_buffer_input regarding buffer size we pass + to send and recv. */ + +#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( void *closure ) +{ + /* Nothing to do. Sockets are always flushed. */ + return 0; +} + + + +static int +socket_buffer_shutdown( struct buffer *buf ) +{ + struct socket_buffer *n = (struct socket_buffer *) buf->closure; + char tmp; + + /* no need to flush children of an endpoint buffer here */ + + if (buf->input) + { + int err = 0; + if (! buf_empty_p (buf) + || (err = recv (n->socket, &tmp, 1, 0)) > 0) + error (0, 0, "dying gasps from %s unexpected", current_parsed_root->hostname); + else if (err == -1) + error (0, 0, "reading from %s: %s", current_parsed_root->hostname, SOCK_STRERROR (SOCK_ERRNO)); + + /* shutdown() socket */ +# ifdef SHUTDOWN_SERVER + if (current_parsed_root->method != server_method) +# endif + if (shutdown (n->socket, 0) < 0) + { + error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); + } + + buf->input = NULL; + } + else if (buf->output) + { + /* shutdown() socket */ +# ifdef SHUTDOWN_SERVER + /* FIXME: Should have a SHUTDOWN_SERVER_INPUT & + * SHUTDOWN_SERVER_OUTPUT + */ + if (current_parsed_root->method == server_method) + SHUTDOWN_SERVER (n->socket); + else +# endif + if (shutdown (n->socket, 1) < 0) + { + error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); + } + + buf->output = NULL; + } + + return 0; +} + +#endif /* NO_SOCKET_TO_FD */ + +#endif /* CLIENT_SUPPORT */ diff --git a/contrib/cvs-1.12.9/src/socket-client.h b/contrib/cvs-1.12.9/src/socket-client.h new file mode 100644 index 0000000000..0705d063d4 --- /dev/null +++ b/contrib/cvs-1.12.9/src/socket-client.h @@ -0,0 +1,57 @@ +/* CVS socket client stuff. + + 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. */ + +#ifndef SOCKET_CLIENT_H__ +#define SOCKET_CLIENT_H__ 1 + +#include + +struct buffer *socket_buffer_initialize + (int, int, void (*) (struct buffer *)); + +# if defined(AUTH_CLIENT_SUPPORT) || defined(HAVE_KERBEROS) || defined(HAVE_GSSAPI) || defined(SOCK_ERRNO) || defined(SOCK_STRERROR) +# ifdef HAVE_WINSOCK_H +# include +# else /* No winsock.h */ +# include +# include +# include +# include +# endif /* No winsock.h */ +# endif + +/* 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 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 +# endif + +# ifndef strerror +extern char *strerror (int); +# endif +# endif /* ! SOCK_STRERROR */ + +struct hostent *init_sockaddr (struct sockaddr_in *, char *, unsigned int); + +#endif /* SOCKET_CLIENT_H__ */ diff --git a/contrib/cvs-1.12.9/src/stack.c b/contrib/cvs-1.12.9/src/stack.c new file mode 100644 index 0000000000..6b5b298a8b --- /dev/null +++ b/contrib/cvs-1.12.9/src/stack.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2004, Free Software Foundation, + * Derek Price, + * & Ximbiot . + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * This module uses the hash.c module to implement a stack. + */ + +#include "cvs.h" +#include + + + +static void +do_push (List *stack, void *elem, int isstring) +{ + Node *p = getnode(); + + if (isstring) + p->key = elem; + else + p->data = elem; + + addnode(stack, p); +} + + + +void +push (List *stack, void *elem) +{ + do_push (stack, elem, 0); +} + + + +void +push_string (List *stack, char *elem) +{ + do_push (stack, elem, 1); +} + + + +static void * +do_pop (List *stack, int isstring) +{ + void *elem; + + if (isempty (stack)) return NULL; + + if (isstring) + { + elem = stack->list->prev->key; + stack->list->prev->key = NULL; + } + else + { + elem = stack->list->prev->data; + stack->list->prev->data = NULL; + } + + delnode (stack->list->prev); + return elem; +} + + + +void * +pop (List *stack) +{ + return do_pop (stack, 0); +} + + + +char * +pop_string (List *stack) +{ + return do_pop (stack, 1); +} + + + +static void +do_unshift (List *stack, void *elem, int isstring) +{ + Node *p = getnode(); + + if (isstring) + p->key = elem; + else + p->data = elem; + + addnode_at_front(stack, p); +} + + + +void +unshift (List *stack, void *elem) +{ + do_unshift (stack, elem, 0); +} + + + +void +unshift_string (List *stack, char *elem) +{ + do_unshift (stack, elem, 1); +} + + + +static void * +do_shift (List *stack, int isstring) +{ + void *elem; + + if (isempty (stack)) return NULL; + + if (isstring) + { + elem = stack->list->next->key; + stack->list->next->key = NULL; + } + else + { + elem = stack->list->next->data; + stack->list->next->data = NULL; + } + delnode (stack->list->next); + return elem; +} + + + +void * +shift (List *stack) +{ + return do_shift (stack, 0); +} + + + +char * +shift_string (List *stack) +{ + return do_shift (stack, 1); +} + + + +int +isempty (List *stack) +{ + if (stack->list == stack->list->next) + return 1; + return 0; +} diff --git a/contrib/cvs-1.12.9/src/stack.h b/contrib/cvs-1.12.9/src/stack.h new file mode 100644 index 0000000000..d49239b418 --- /dev/null +++ b/contrib/cvs-1.12.9/src/stack.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2004, Free Software Foundation, + * Derek Price, + * & Ximbiot . + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + */ + +void push (List *_stack, void *_elem); +void *pop (List *_stack); +void unshift (List *_stack, void *_elem); +void *shift (List *_stack); +void push_string (List *_stack, char *_elem); +char *pop_string (List *_stack); +void unshift_string (List *_stack, char *_elem); +char *shift_string (List *_stack); +int isempty (List *_stack); diff --git a/contrib/cvs-1.12.9/src/status.c b/contrib/cvs-1.12.9/src/status.c new file mode 100644 index 0000000000..907c08da59 --- /dev/null +++ b/contrib/cvs-1.12.9/src/status.c @@ -0,0 +1,359 @@ +/* + * 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 source distribution. + * + * Status Information + */ + +#include "cvs.h" + +static Dtype status_dirproc (void *callerdat, const char *dir, + const char *repos, const char *update_dir, + List *entries); +static int status_fileproc (void *callerdat, struct file_info *finfo); +static int tag_list_proc (Node * p, void *closure); + +static int local = 0; +static int long_format = 0; +static RCSNode *xrcsnode; + +static const char *const status_usage[] = +{ + "Usage: %s %s [-vlR] [files...]\n", + "\t-v\tVerbose format; includes tag information for the file\n", + "\t-l\tProcess this directory only (not recursive).\n", + "\t-R\tProcess directories recursively.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +cvsstatus (int argc, char **argv) +{ + int c; + int err = 0; + + if (argc == -1) + usage (status_usage); + + optind = 0; + while ((c = getopt (argc, argv, "+vlR")) != -1) + { + switch (c) + { + case 'v': + long_format = 1; + break; + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case '?': + default: + usage (status_usage); + break; + } + } + argc -= optind; + argv += optind; + + wrap_setup (); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + start_server (); + + ign_setup (); + + if (long_format) + send_arg("-v"); + if (local) + send_arg("-l"); + send_arg ("--"); + + /* For a while, we tried setting SEND_NO_CONTENTS here so this + could be a fast operation. That prevents the + server from updating our timestamp if the timestamp is + changed but the file is unmodified. Worse, it is user-visible + (shows "locally modified" instead of "up to date" if + timestamp is changed but file is not). And there is no good + workaround (you might not want to run "cvs update"; "cvs -n + update" doesn't update CVS/Entries; "cvs diff --brief" or + something perhaps could be made to work but somehow that + seems nonintuitive to me even if so). Given that timestamps + seem to have the potential to get munged for any number of + reasons, it seems better to not rely too much on them. */ + + send_files (argc, argv, local, 0, 0); + + send_file_names (argc, argv, SEND_EXPAND_WILD); + + send_to_server ("status\012", 0); + err = get_responses_and_close (); + + return err; + } +#endif + + /* start the recursion processor */ + err = start_recursion + ( status_fileproc, (FILESDONEPROC) NULL, + status_dirproc, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, + W_LOCAL, 0, CVS_LOCK_READ, (char *) NULL, 1, (char *) NULL); + + return (err); +} + +/* + * display the status of a file + */ +/* ARGSUSED */ +static int +status_fileproc (void *callerdat, struct file_info *finfo) +{ + Ctype status; + char *sstat; + Vers_TS *vers; + + status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL, + 1, 0, &vers, 0); + sstat = "Classify Error"; + switch (status) + { + case T_UNKNOWN: + sstat = "Unknown"; + break; + case T_CHECKOUT: + sstat = "Needs Checkout"; + break; + case T_PATCH: + sstat = "Needs Patch"; + break; + case T_CONFLICT: + /* FIXME - This message needs to be clearer. It comes up now + * only when a file exists or has been added in the local sandbox + * and a file of the same name has been committed indepenently to + * the repository from a different sandbox. It also comes up + * whether an update has been attempted or not, so technically, I + * think it is not actually a conflict yet. + */ + sstat = "Unresolved Conflict"; + break; + case T_ADDED: + sstat = "Locally Added"; + break; + case T_REMOVED: + sstat = "Locally Removed"; + break; + case T_MODIFIED: + if ( vers->ts_conflict + && ( file_has_conflict ( finfo, vers->ts_conflict ) + || file_has_markers ( finfo ) ) ) + sstat = "File had conflicts on merge"; + else + /* Note that we do not re Register() the file when we spot + * a resolved conflict like update_fileproc() does on the + * premise that status should not alter the sandbox. + */ + sstat = "Locally Modified"; + break; + case T_REMOVE_ENTRY: + sstat = "Entry Invalid"; + break; + case T_UPTODATE: + sstat = "Up-to-date"; + break; + case T_NEEDS_MERGE: + sstat = "Needs Merge"; + break; + case T_TITLE: + /* I don't think this case can occur here. Just print + "Classify Error". */ + break; + } + + cvs_output ("\ +===================================================================\n", 0); + if (vers->ts_user == NULL) + { + 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 + { + 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) + { + 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') + cvs_output (" Working revision:\tNew file!\n", 0); +#ifdef SERVER_SUPPORT + else if (server_active) + { + cvs_output (" Working revision:\t", 0); + cvs_output (vers->vn_user, 0); + cvs_output ("\n", 0); + } +#endif + else + { + 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) + cvs_output (" Repository revision:\tNo revision control file\n", 0); + else + { + 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) + { + Entnode *edata; + + edata = vers->entdata; + if (edata->tag) + { + if (vers->vn_rcs == NULL) + { + cvs_output (" Sticky Tag:\t\t", 0); + cvs_output (edata->tag, 0); + cvs_output (" - MISSING from RCS file!\n", 0); + } + else + { + if (isdigit ((unsigned char) edata->tag[0])) + { + cvs_output (" Sticky Tag:\t\t", 0); + cvs_output (edata->tag, 0); + cvs_output ("\n", 0); + } + else + { + char *branch = NULL; + + if (RCS_nodeisbranch (finfo->rcs, edata->tag)) + branch = RCS_whatbranch(finfo->rcs, edata->tag); + + 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); + } + } + } + else if (!really_quiet) + cvs_output (" Sticky Tag:\t\t(none)\n", 0); + + if (edata->date) + { + cvs_output (" Sticky Date:\t\t", 0); + cvs_output (edata->date, 0); + cvs_output ("\n", 0); + } + else if (!really_quiet) + cvs_output (" Sticky Date:\t\t(none)\n", 0); + + if (edata->options && edata->options[0]) + { + cvs_output (" Sticky Options:\t", 0); + cvs_output (edata->options, 0); + cvs_output ("\n", 0); + } + else if (!really_quiet) + cvs_output (" Sticky Options:\t(none)\n", 0); + } + + if (long_format && vers->srcfile) + { + List *symbols = RCS_symbols(vers->srcfile); + + cvs_output ("\n Existing Tags:\n", 0); + if (symbols) + { + xrcsnode = finfo->rcs; + (void) walklist (symbols, tag_list_proc, NULL); + } + else + cvs_output ("\tNo Tags Exist\n", 0); + } + + cvs_output ("\n", 0); + freevers_ts (&vers); + return (0); +} + + + +/* + * Print a warm fuzzy message + */ +/* ARGSUSED */ +static Dtype +status_dirproc (void *callerdat, const char *dir, const char *repos, + const char *update_dir, List *entries) +{ + if (!quiet) + error (0, 0, "Examining %s", update_dir); + return (R_PROCESS); +} + + + +/* + * Print out a tag and its type + */ +static int +tag_list_proc (Node *p, void *closure) +{ + char *branch = NULL; + char *buf; + + if (RCS_nodeisbranch (xrcsnode, p->key)) + branch = RCS_whatbranch(xrcsnode, p->key) ; + + 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 : (char *)p->data); + cvs_output (buf, 0); + free (buf); + + if (branch) + free (branch); + + return (0); +} diff --git a/contrib/cvs-1.12.9/src/subr.c b/contrib/cvs-1.12.9/src/subr.c new file mode 100644 index 0000000000..daa4c3fad3 --- /dev/null +++ b/contrib/cvs-1.12.9/src/subr.c @@ -0,0 +1,1838 @@ +/* + * 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 source distribution. + * + * Various useful functions for the CVS support code. + */ + +#include "cvs.h" +#include "getline.h" + +/* Get wint_t. */ +#ifdef HAVE_WINT_T +# include +#endif + +#if !defined HAVE_NANOSLEEP && !defined HAVE_USLEEP && defined HAVE_SELECT + /* use select as a workaround */ +# include "xselect.h" +#endif /* !defined HAVE_NANOSLEEP && !defined HAVE_USLEEP && defined HAVE_SELECT */ + +extern char *getlogin (void); + + + +/* *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 (char **strptr, size_t *n, size_t newsize) +{ + while (*n < newsize) + *strptr = x2realloc (*strptr, n); +} + + + +/* *STR is a pointer to a malloc'd string or NULL. *LENP is its allocated + * length. If *STR is NULL then *LENP must be 0 and visa-versa. + * Add SRC to the end of *STR, reallocating *STR if necessary. */ +void +xrealloc_and_strcat (char **str, size_t *lenp, const char *src) +{ + short newstr = !*lenp; + expand_string (str, lenp, (newstr ? 0 : strlen (*str)) + strlen (src) + 1); + if (newstr) + strcpy (*str, src); + else + strcat (*str, src); +} + + + +/* Remove trailing newlines from STRING, destructively. + * + * RETURNS + * + * True if any newlines were removed, false otherwise. + */ +int +strip_trailing_newlines (char *str) +{ + size_t index, origlen; + index = origlen = strlen (str); + + while (index > 0 && str[index-1] == '\n') + str[--index] = '\0'; + + return index != origlen; +} + + + +/* Return the number of levels that PATH ascends above where it starts. + * For example: + * + * "../../foo" -> 2 + * "foo/../../bar" -> 1 + */ +int +pathname_levels (const char *p) +{ + int level; + int max_level; + + if (p == NULL) return 0; + + max_level = 0; + level = 0; + do + { + /* Now look for pathname level-ups. */ + if (p[0] == '.' && p[1] == '.' && (p[2] == '\0' || ISSLASH (p[2]))) + { + --level; + if (-level > max_level) + max_level = -level; + } + else if (p[0] == '\0' || ISSLASH (p[0]) || + (p[0] == '.' && (p[1] == '\0' || ISSLASH (p[1])))) + ; + else + ++level; + + /* q = strchr (p, '/'); but sub ISSLASH() for '/': */ + while (*p != '\0' && !ISSLASH (*p)) p++; + if (*p != '\0') p++; + } while (*p != '\0'); + return max_level; +} + + + +/* Free a vector, where (*ARGV)[0], (*ARGV)[1], ... (*ARGV)[*PARGC - 1] + are malloc'd and so is *ARGV itself. Such a vector is allocated by + line2argv or expand_wild, for example. */ +void +free_names (int *pargc, char **argv) +{ + register int i; + + for (i = 0; i < *pargc; i++) + { /* only do through *pargc */ + free (argv[i]); + } + free (argv); + *pargc = 0; /* and set it to zero when done */ +} + +/* Convert LINE into arguments separated by SEPCHARS. 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 (int *pargc, char ***argv, char *line, char *sepchars) +{ + 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 = 1; + *argv = (char **) xmalloc (argv_allocated * sizeof (**argv)); + + *pargc = 0; + for (cp = strtok (line, sepchars); cp; cp = strtok ((char *) NULL, sepchars)) + { + if (*pargc == argv_allocated) + { + argv_allocated *= 2; + *argv = xrealloc (*argv, argv_allocated * sizeof (**argv)); + } + (*argv)[*pargc] = xstrdup (cp); + (*pargc)++; + } +} + +/* + * Returns the number of dots ('.') found in an RCS revision number + */ +int +numdots (const char *s) +{ + int dots = 0; + + for (; *s; s++) + { + if (*s == '.') + dots++; + } + return (dots); +} + +/* Compare revision numbers REV1 and REV2 by consecutive fields. + Return negative, zero, or positive in the manner of strcmp. The + two revision numbers must have the same number of fields, or else + compare_revnums will return an inaccurate result. */ +int +compare_revnums (const char *rev1, const char *rev2) +{ + const char *sp, *tp; + char *snext, *tnext; + int result = 0; + + sp = rev1; + tp = rev2; + while (result == 0) + { + result = strtoul (sp, &snext, 10) - strtoul (tp, &tnext, 10); + if (*snext == '\0' || *tnext == '\0') + break; + sp = snext + 1; + tp = tnext + 1; + } + + return result; +} + +/* Increment a revision number. Working on the string is a bit awkward, + but it avoid problems with integer overflow should the revision numbers + get really big. */ +char * +increment_revnum (const char *rev) +{ + char *newrev, *p; + int lastfield; + size_t len = strlen (rev); + + newrev = xmalloc (len + 2); + memcpy (newrev, rev, len + 1); + for (p = newrev + len; p != newrev; ) + { + --p; + if (!isdigit(*p)) + { + ++p; + break; + } + if (*p != '9') + { + ++*p; + return newrev; + } + *p = '0'; + } + /* The number was all 9s, so change the first character to 1 and add + a 0 to the end. */ + *p = '1'; + p = newrev + len; + *p++ = '0'; + *p = '\0'; + return newrev; +} + +/* Return the username by which the caller should be identified in + CVS, in contexts such as the author field of RCS files, various + logs, etc. */ +char * +getcaller (void) +{ +#ifndef SYSTEM_GETCALLER + static char *cache; + struct passwd *pw; + uid_t uid; +#endif + + /* If there is a CVS username, return it. */ +#ifdef AUTH_SERVER_SUPPORT + if (CVS_Username != NULL) + return CVS_Username; +#endif + +#ifdef SYSTEM_GETCALLER + return SYSTEM_GETCALLER (); +#else + /* Get the caller's login from his uid. If the real uid is "root" + try LOGNAME USER or getlogin(). If getlogin() and getpwuid() + both fail, return the uid as a string. */ + + if (cache != NULL) + return cache; + + uid = getuid (); + if (uid == (uid_t) 0) + { + char *name; + + /* super-user; try getlogin() to distinguish */ + if (((name = getlogin ()) || (name = getenv("LOGNAME")) || + (name = getenv("USER"))) && *name) + { + cache = xstrdup (name); + return cache; + } + } + if ((pw = (struct passwd *) getpwuid (uid)) == NULL) + { + char uidname[20]; + + (void) sprintf (uidname, "uid%lu", (unsigned long) uid); + cache = xstrdup (uidname); + return cache; + } + cache = xstrdup (pw->pw_name); + return cache; +#endif +} + +#ifdef lint +#ifndef __GNUC__ +/* ARGSUSED */ +time_t +get_date( char *date, struct timeb *now ) +{ + time_t foo = 0; + + return (foo); +} +#endif +#endif + + + +/* Given some revision, REV, return the first prior revision that exists in the + * RCS file, RCS. + * + * ASSUMPTIONS + * REV exists. + * + * INPUTS + * RCS The RCS node pointer. + * REV An existing revision in the RCS file referred to by RCS. + * + * RETURNS + * The first prior revision that exists in the RCS file, or NULL if no prior + * revision exists. The caller is responsible for disposing of this string. + * + * NOTES + * This function currently neglects the case where we are on the trunk with + * rev = X.1, where X != 1. If rev = X.Y, where X != 1 and Y > 1, then this + * function should work fine, as revision X.1 must exist, due to RCS rules. + */ +char * +previous_rev (rcs, rev) + RCSNode *rcs; + const char *rev; +{ + char *p; + char *tmp = xstrdup (rev); + long r1; + char *retval; + + /* Our retval can have no more digits and dots than our input revision. */ + retval = xmalloc (strlen (rev) + 1); + p = strrchr (tmp, '.'); + *p = '\0'; + r1 = strtol (p+1, NULL, 10); + do { + if (--r1 == 0) + { + /* If r1 == 0, then we must be on a branch and our parent must + * exist, or we must be on the trunk with a REV like X.1. + * We are neglecting the X.1 with X != 1 case by assuming that + * there is no previous revision when we discover we were on + * the trunk. + */ + p = strrchr (tmp, '.'); + if (p == NULL) + /* We are on the trunk. */ + retval = NULL; + else + { + *p = '\0'; + sprintf (retval, "%s", tmp); + } + break; + } + sprintf (retval, "%s.%ld", tmp, r1); + } while (!RCS_exist_rev (rcs, retval)); + + free (tmp); + return retval; +} + + + +/* Given two revisions, find their greatest common ancestor. If the + two input revisions exist, then rcs guarantees that the gca will + exist. */ + +char * +gca (const char *rev1, const char *rev2) +{ + int dots; + char *gca, *g; + const char *p1, *p2; + int r1, r2; + char *retval; + + if (rev1 == NULL || rev2 == NULL) + { + error (0, 0, "sanity failure in gca"); + 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. */ + g = gca = xmalloc (strlen (rev1) + strlen (rev2) + 100); + + /* walk the strings, reading the common parts. */ + p1 = rev1; + p2 = rev2; + do + { + r1 = strtol (p1, (char **) &p1, 10); + r2 = strtol (p2, (char **) &p2, 10); + + /* use the lowest. */ + (void) sprintf (g, "%d.", r1 < r2 ? r1 : r2); + g += strlen (g); + if (*p1 == '.') ++p1; + else break; + if (*p2 == '.') ++p2; + else break; + } while (r1 == r2); + + /* erase that last dot. */ + *--g = '\0'; + + /* numbers differ, or we ran out of strings. we're done with the + common parts. */ + + dots = numdots (gca); + if (dots == 0) + { + /* revisions differ in trunk major number. */ + + if (r2 < r1) p1 = p2; + if (*p1 == '\0') + { + /* we only got one number. this is strange. */ + error (0, 0, "bad revisions %s or %s", rev1, rev2); + abort(); + } + else + { + /* we have a minor number. use it. */ + *g++ = '.'; + while (*p1 != '.' && *p1 != '\0') + *g++ = *p1++; + *g = '\0'; + } + } + else if ((dots & 1) == 0) + { + /* if we have an even number of dots, then we have a branch. + remove the last number in order to make it a revision. */ + + g = strrchr (gca, '.'); + *g = '\0'; + } + + retval = xstrdup (gca); + free (gca); + return retval; +} + +/* Give fatal error if REV is numeric and ARGC,ARGV imply we are + planning to operate on more than one file. The current directory + should be the working directory. Note that callers assume that we + will only be checking the first character of REV; it need not have + '\0' at the end of the tag name and other niceties. Right now this + is only called from admin.c, but if people like the concept it probably + should also be called from diff -r, update -r, get -r, and log -r. */ + +void +check_numeric (const char *rev, int argc, char **argv) +{ + if (rev == NULL || !isdigit ((unsigned char) *rev)) + return; + + /* Note that the check for whether we are processing more than one + file is (basically) syntactic; that is, we don't behave differently + depending on whether a directory happens to contain only a single + file or whether it contains more than one. I strongly suspect this + is the least confusing behavior. */ + if (argc != 1 + || (!wrap_name_has (argv[0], WRAP_TOCVS) && isdir (argv[0]))) + { + error (0, 0, "while processing more than one file:"); + error (1, 0, "attempt to specify a numeric revision"); + } +} + +/* + * Sanity checks and any required fix-up on message passed to RCS via '-m'. + * RCS 5.7 requires that a non-total-whitespace, non-null message be provided + * with '-m'. Returns a newly allocated, non-empty buffer with whitespace + * stripped from end of lines and end of buffer. + * + * TODO: We no longer use RCS to manage repository files, so maybe this + * nonsense about non-empty log fields can be dropped. + */ +char * +make_message_rcsvalid (const char *message) +{ + char *dst, *dp; + const char *mp; + + if (message == NULL) message = ""; + + /* Strip whitespace from end of lines and end of string. */ + dp = dst = (char *) xmalloc (strlen (message) + 1); + for (mp = message; *mp != '\0'; ++mp) + { + if (*mp == '\n') + { + /* At end-of-line; backtrack to last non-space. */ + while (dp > dst && (dp[-1] == ' ' || dp[-1] == '\t')) + --dp; + } + *dp++ = *mp; + } + + /* Backtrack to last non-space at end of string, and truncate. */ + while (dp > dst && isspace ((unsigned char) dp[-1])) + --dp; + *dp = '\0'; + + /* After all that, if there was no non-space in the string, + substitute a non-empty message. */ + if (*dst == '\0') + { + free (dst); + dst = xstrdup ("*** empty log message ***"); + } + + return dst; +} + + + +/* + * file_has_conflict + * + * This function compares the timestamp of a file with ts_conflict set + * to the timestamp on the actual file and returns TRUE or FALSE based + * on the results. + * + * This function does not check for actual markers in the file and + * file_has_markers() function should be called when that is interesting. + * + * ASSUMPTIONS + * The ts_conflict field is not NULL. + * + * RETURNS + * TRUE ts_conflict matches the current timestamp. + * FALSE The ts_conflict field does not match the file's + * timestamp. + */ +int +file_has_conflict (const struct file_info *finfo, const char *ts_conflict) +{ + char *filestamp; + int retcode; + + /* If ts_conflict is NULL, there was no merge since the last + * commit and there can be no conflict. + */ + assert (ts_conflict); + + /* + * If the timestamp has changed and no + * conflict indicators are found, it isn't a + * conflict any more. + */ + +#ifdef SERVER_SUPPORT + if (server_active) + retcode = ts_conflict[0] == '=' && ts_conflict[1] == '\0'; + else +#endif /* SERVER_SUPPORT */ + { + filestamp = time_stamp (finfo->file); + retcode = !strcmp (ts_conflict, filestamp); + free (filestamp); + } + + return retcode; +} + + + +/* 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 (const 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_1, sizeof RCS_MERGE_PAT_1 - 1) == 0 || + strncmp (line, RCS_MERGE_PAT_2, sizeof RCS_MERGE_PAT_2 - 1) == 0 || + strncmp (line, RCS_MERGE_PAT_3, sizeof RCS_MERGE_PAT_3 - 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; +} + +/* Read the entire contents of the file NAME into *BUF. + If NAME is NULL, read from stdin. *BUF + is a pointer returned from malloc (or NULL), pointing to *BUFSIZE + bytes of space. The actual size is returned in *LEN. On error, + give a fatal error. The name of the file to use in error messages + (typically will include a directory if we have changed directory) + is FULLNAME. MODE is "r" for text or "rb" for binary. */ + +void +get_file (const char *name, const char *fullname, const char *mode, char **buf, size_t *bufsize, size_t *len) +{ + struct stat s; + size_t nread; + char *tobuf; + FILE *e; + size_t filesize; + + if (name == NULL) + { + e = stdin; + filesize = 100; /* force allocation of minimum buffer */ + } + else + { + /* Although it would be cleaner in some ways to just read + until end of file, reallocating the buffer, this function + does get called on files in the working directory which can + be of arbitrary size, so I think we better do all that + extra allocation. */ + + if (CVS_STAT (name, &s) < 0) + error (1, errno, "can't stat %s", fullname); + + /* Convert from signed to unsigned. */ + filesize = s.st_size; + + e = open_file (name, mode); + } + + if (*buf == NULL || *bufsize <= filesize) + { + *bufsize = filesize + 1; + *buf = xrealloc (*buf, *bufsize); + } + + tobuf = *buf; + nread = 0; + while (1) + { + size_t got; + + got = fread (tobuf, 1, *bufsize - (tobuf - *buf), e); + if (ferror (e)) + error (1, errno, "can't read %s", fullname); + nread += got; + tobuf += got; + + if (feof (e)) + break; + + /* Allocate more space if needed. */ + if (tobuf == *buf + *bufsize) + { + int c; + long off; + + c = getc (e); + if (c == EOF) + break; + off = tobuf - *buf; + expand_string (buf, bufsize, *bufsize + 100); + tobuf = *buf + off; + *tobuf++ = c; + ++nread; + } + } + + if (e != stdin && fclose (e) < 0) + error (0, errno, "cannot close %s", fullname); + + *len = nread; + + /* Force *BUF to be large enough to hold a null terminator. */ + if (nread == *bufsize) + expand_string (buf, bufsize, *bufsize + 1); + (*buf)[nread] = '\0'; +} + + +/* Follow a chain of symbolic links to its destination. FILENAME + should be a handle to a malloc'd block of memory which contains the + beginning of the chain. This routine will replace the contents of + FILENAME with the destination (a real file). */ + +void +resolve_symlink (char **filename) +{ + if (filename == NULL || *filename == NULL) + return; + + while (islink (*filename)) + { +#ifdef HAVE_READLINK + /* The clean thing to do is probably to have each filesubr.c + implement this (with an error if not supported by the + platform, in which case islink would presumably return 0). + But that would require editing each filesubr.c and so the + expedient hack seems to be looking at HAVE_READLINK. */ + char *newname = xreadlink (*filename); + + if (isabsolute (newname)) + { + free (*filename); + *filename = newname; + } + else + { + const char *oldname = last_component (*filename); + int dirlen = oldname - *filename; + char *fullnewname = xmalloc (dirlen + strlen (newname) + 1); + strncpy (fullnewname, *filename, dirlen); + strcpy (fullnewname + dirlen, newname); + free (newname); + free (*filename); + *filename = fullnewname; + } +#else + error (1, 0, "internal error: islink doesn't like readlink"); +#endif + } +} + +/* + * Rename a file to an appropriate backup name based on BAKPREFIX. + * If suffix non-null, then "." is appended to the new name. + * + * Returns the new name, which caller may free() if desired. + */ +char * +backup_file (const char *filename, const char *suffix) +{ + char *backup_name; + + if (suffix == NULL) + { + backup_name = xmalloc (sizeof (BAKPREFIX) + strlen (filename) + 1); + sprintf (backup_name, "%s%s", BAKPREFIX, filename); + } + else + { + backup_name = xmalloc (sizeof (BAKPREFIX) + + strlen (filename) + + strlen (suffix) + + 2); /* one for dot, one for trailing '\0' */ + sprintf (backup_name, "%s%s.%s", BAKPREFIX, filename, suffix); + } + + if (isfile (filename)) + copy_file (filename, backup_name); + + return backup_name; +} + +/* + * Copy a string into a buffer escaping any shell metacharacters. The + * buffer should be at least twice as long as the string. + * + * Returns a pointer to the terminating NUL byte in buffer. + */ + +char * +shell_escape(char *buf, const char *str) +{ + static const char meta[] = "$`\\\""; + const char *p; + + for (;;) + { + p = strpbrk(str, meta); + if (!p) p = str + strlen(str); + if (p > str) + { + memcpy(buf, str, p - str); + buf += p - str; + } + if (!*p) break; + *buf++ = '\\'; + *buf++ = *p++; + str = p; + } + *buf = '\0'; + return buf; +} + + + +/* + * We can only travel forwards in time, not backwards. :) + */ +void +sleep_past (time_t desttime) +{ + time_t t; + long s; + long us; + + while (time (&t) <= desttime) + { +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + gettimeofday (&tv, NULL); + if (tv.tv_sec > desttime) + break; + s = desttime - tv.tv_sec; + if (tv.tv_usec > 0) + us = 1000000 - tv.tv_usec; + else + { + s++; + us = 0; + } +#else + /* default to 20 ms increments */ + s = desttime - t; + us = 20000; +#endif + + { + struct timespec ts; + ts.tv_sec = s; + ts.tv_nsec = us * 1000; + (void)nanosleep (&ts, NULL); + } + } +} + + + +/* used to store callback data in a list indexed by the user format string + */ +typedef int (*CONVPROC_t) (Node *, void *); +struct cmdline_bindings +{ + char conversion; + void *data; + CONVPROC_t convproc; + void *closure; +}; +/* since we store the above in a list, we need to dispose of the data field. + * we don't have to worry about convproc or closure since pointers are stuck + * in there directly and format_cmdline's caller is responsible for disposing + * of those if necessary. + */ +static void +cmdline_bindings_hash_node_delete (Node *p) +{ + struct cmdline_bindings *b = p->data; + + if (b->conversion != ',') + { + free (b->data); + } + free (b); +} + + + +/* + * assume s is a literal argument and put it between quotes, + * escaping as appropriate for a shell command line + * + * the caller is responsible for disposing of the new string + */ +char * +cmdlinequote (char quotes, char *s) +{ + char *quoted = cmdlineescape (quotes, s); + char *buf = xmalloc(strlen(quoted)+3); + + buf[0] = quotes; + buf[1] = '\0'; + strcat (buf, quoted); + free (quoted); + buf[strlen(buf)+1] = '\0'; + buf[strlen(buf)] = quotes; + return buf; +} + + + +/* read quotes as the type of quotes we are between (if any) and then make our + * argument so it could make it past a cmdline parser (using sh as a model) + * inside the quotes (if any). + * + * if you were planning on expanding any paths, it should be done before + * calling this function, as it escapes shell metacharacters. + * + * the caller is responsible for disposing of the new string + * + * FIXME: See about removing/combining this functionality with shell_escape() + * in subr.c. + */ +char * +cmdlineescape (char quotes, char *s) +{ + char *buf = NULL; + size_t length = 0; + char *d = NULL; + size_t doff; + char *lastspace; + + lastspace = s - 1; + do + { + /* FIXME: Single quotes only require other single quotes to be escaped + * for Bourne Shell. + */ + if ( isspace( *s ) ) lastspace = s; + if( quotes + ? ( *s == quotes + || ( quotes == '"' + && ( *s == '$' || *s == '`' || *s == '\\' ) ) ) + : ( strchr( "\\$`'\"*?", *s ) + || isspace( *s ) + || ( lastspace == ( s - 1 ) + && *s == '~' ) ) ) + { + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + *d++ = '\\'; + } + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + } while ((*d++ = *s++) != '\0'); + return (buf); +} + + + +/* expand format strings in a command line. modeled roughly after printf + * + * this function's arg list must be NULL terminated + * + * assume a space delimited list of args is the desired final output, + * but args can be quoted (" or '). + * + * the best usage examples are in tag.c & logmsg.c, but here goes: + * + * INPUTS + * int oldway to support old format strings + * char *srepos you guessed it + * char *format the format string to parse + * ... NULL terminated data list in the following format: + * char *userformat, char *printfformat, data + * where + * char *userformat a list of possible + * format characters the + * end user might pass us + * in the format string + * (e.g. those found in + * taginfo or loginfo) + * multiple characters in + * this strings will be + * aliases for each other + * char *printfformat the same list of args + * printf uses to + * determine what kind of + * data the next arg will + * be + * data a piece of data to be + * formatted into the user + * string, + * determined by the + * printfformat string. + * or + * char *userformat, char *printfformat, List *data, + * int (*convproc) (Node *, void *), void *closure + * where + * char *userformat same as above, except + * multiple characters in + * this string represent + * different node + * attributes which can be + * retrieved from data by + * convproc + * char *printfformat = "," + * List *data the list to be walked + * with walklist & + * convproc to retrieve + * data for each of the + * possible format + * characters in + * userformat + * int (*convproc)() see data + * void *closure arg to be passed into + * walklist as closure + * data for convproc + * + * EXAMPLE + * (ignoring oldway variable and srepos since those are only around while we + * SUPPORT_OLD_INFO_FMT_STRINGS) + * format_cmdline( "/cvsroot/CVSROOT/mytaginfoproc %t %o %{sVv}", + * "t", "s", "newtag", + * "o", "s", "mov", + * "xG", "ld", longintwhichwontbeusedthispass, + * "sVv", ",", tlist, pretag_list_to_args_proc, + * (void *) mydata, + * (char *)NULL); + * + * would generate the following command line, assuming two files in tlist, + * file1 & file2, each with old versions 1.1 and new version 1.1.2.3: + * + * /cvsroot/CVSROOT/mytaginfoproc "newtag" "mov" "file1" "1.1" "1.1.2.3" "file2" "1.1" "1.1.2.3" + * + * RETURNS + * pointer to newly allocated string. the caller is responsible for + * disposing of this string. + */ +char * +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS +format_cmdline (int oldway, const char *srepos, const char *format, ...) +#else /* SUPPORT_OLD_INFO_FMT_STRINGS */ +format_cmdline (const char *format, ...) +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ +{ + va_list args; /* our input function args */ + char *buf; /* where we store our output string */ + size_t length; /* the allocated length of our output string in bytes. + * used as a temporary storage for the length of the + * next function argument during function + * initialization + */ + char *pfmt; /* initially the list of fmt keys passed in, + * but used as a temporary key buffer later + */ + char *fmt; /* buffer for format string which we are processing */ + size_t flen; /* length of fmt buffer */ + char *d, *q, *r; /* for walking strings */ + const char *s; + size_t doff, qoff; + char inquotes; + + List *pflist = getlist(); /* our list of input data indexed by format + * "strings" + */ + Node *p; + struct cmdline_bindings *b; + static int warned_of_deprecation = 0; + char key[] = "?"; /* Used as temporary storage for a single + * character search string used to locate a + * hash key. + */ +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + /* state varialbes in the while loop which parses the actual + * format string in the final parsing pass*/ + int onearg; + int subbedsomething; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (oldway && !warned_of_deprecation) + { + /* warn the user that we don't like his kind 'round these parts */ + warned_of_deprecation = 1; + error (0, 0, +"warning: Set to use deprecated info format strings. Establish\n" +"compatibility with the new info file format strings (add a temporary '1' in\n" +"all info files after each '%%' which doesn't represent a literal percent)\n" +"and set UseNewInfoFmtStrings=yes in CVSROOT/config. After that, convert\n" +"individual command lines and scripts to handle the new format at your\n" +"leisure."); + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + + va_start (args, format); + + /* read our possible format strings + * expect a certain number of arguments by type and a NULL format + * string to terminate the list. + */ + while ((pfmt = va_arg (args, char *)) != NULL) + { + char *conversion = va_arg (args, char *); + + char conversion_error = 0; + char char_conversion = 0; + char decimal_conversion = 0; + char integer_conversion = 0; + char string_conversion = 0; + + /* allocate space to save our data */ + b = xmalloc(sizeof(struct cmdline_bindings)); + + /* where did you think we were going to store all this data??? */ + b->convproc = NULL; + b->closure = NULL; + + /* read a length from the conversion string */ + s = conversion; + length = 0; + while (!length && *s) + { + switch (*s) + { + case 'h': + integer_conversion = 1; + if (s[1] == 'h') + { + length = sizeof (char); + s += 2; + } + else + { + char_conversion = 1; + length = sizeof (short); + s++; + } + break; +#ifdef HAVE_INTMAX_T + case 'j': + integer_conversion = 1; + length = sizeof (intmax_t); + s++; + break; +#endif /* HAVE_INTMAX_T */ + case 'l': + integer_conversion = 1; + if (s[1] == 'l') + { +#ifdef HAVE_LONG_LONG + length = sizeof (long long); +#endif + s += 2; + } + else + { + char_conversion = 2; + string_conversion = 2; + length = sizeof (long); + s++; + } + break; + case 't': + integer_conversion = 1; + length = sizeof (ptrdiff_t); + s++; + break; + case 'z': + integer_conversion = 1; + length = sizeof (size_t); + s++; + break; +#ifdef HAVE_LONG_DOUBLE + case 'L': + decimal_conversion = 1; + length = sizeof (long double); + s++; + break; +#endif + default: + char_conversion = 1; + decimal_conversion = 1; + integer_conversion = 1; + string_conversion = 1; + /* take care of it when we find out what we're looking for */ + length = -1; + break; + } + } + /* if we don't have a valid conversion left, that is an error */ + /* read an argument conversion */ + buf = xmalloc (strlen(conversion) + 2); + *buf = '%'; + strcpy (buf+1, conversion); + switch (*s) + { + size_t dummy; + case 'c': + /* chars (an integer conversion) */ + if (!char_conversion) + { + conversion_error = 1; + break; + } + if (char_conversion == 2) + { +#ifdef HAVE_WINT_T + length = sizeof (wint_t); +#else + conversion_error = 1; + break; +#endif + } + else + length = sizeof (char); + /* fall through... */ + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + /* integer conversions */ + if (!integer_conversion) + { + conversion_error = 1; + break; + } + if (length == -1) + { + length = sizeof (int); + } + switch (length) + { + case sizeof(char): + { + char arg_char = (char) va_arg (args, int); + b->data = asnprintf(NULL, &dummy, buf, arg_char); + break; + } +#ifdef UNIQUE_INT_TYPE_WINT_T /* implies HAVE_WINT_T */ + case sizeof(wint_t): + { + wint_t arg_wint_t = va_arg (args, wint_t); + b->data = asnprintf(NULL, &dummy, buf, arg_wint_t); + break; + } +#endif /* UNIQUE_INT_TYPE_WINT_T */ +#ifdef UNIQUE_INT_TYPE_SHORT + case sizeof(short): + { + short arg_short = (short) va_arg (args, int); + b->data = asnprintf(NULL, &dummy, buf, arg_short); + break; + } +#endif /* UNIQUE_INT_TYPE_SHORT */ +#ifdef UNIQUE_INT_TYPE_INT + case sizeof(int): + { + int arg_int = va_arg (args, int); + b->data = asnprintf(NULL, &dummy, buf, arg_int); + break; + } +#endif /* UNIQUE_INT_TYPE_INT */ +#ifdef UNIQUE_INT_TYPE_LONG + case sizeof(long): + { + long arg_long = va_arg (args, long); + b->data = asnprintf(NULL, &dummy, buf, arg_long); + break; + } +#endif /* UNIQUE_INT_TYPE_LONG */ +#ifdef UNIQUE_INT_TYPE_LONG_LONG /* implies HAVE_LONG_LONG */ + case sizeof(long long): + { + long long arg_long_long = va_arg (args, long long); + b->data = asnprintf(NULL, &dummy, buf, arg_long_long); + break; + } +#endif /* UNIQUE_INT_TYPE_LONG_LONG */ +#ifdef UNIQUE_INT_TYPE_INTMAX_T /* implies HAVE_INTMAX_T */ + case sizeof(intmax_t): + { + intmax_t arg_intmax_t = va_arg (args, intmax_t); + b->data = asnprintf(NULL, &dummy, buf, arg_intmax_t); + break; + } +#endif /* UNIQUE_INT_TYPE_INTMAX_T */ +#ifdef UNIQUE_INT_TYPE_SIZE_T + case sizeof(size_t): + { + size_t arg_size_t = va_arg (args, size_t); + b->data = asnprintf(NULL, &dummy, buf, arg_size_t); + break; + } +#endif /* UNIQUE_INT_TYPE_SIZE_T */ +#ifdef UNIQUE_INT_TYPE_PTRDIFF_T + case sizeof(ptrdiff_t): + { + ptrdiff_t arg_ptrdiff_t = va_arg (args, ptrdiff_t); + b->data = asnprintf(NULL, &dummy, buf, arg_ptrdiff_t); + break; + } +#endif /* UNIQUE_INT_TYPE_PTRDIFF_T */ + default: + dellist(&pflist); + free(b); + error (1, 0, +"internal error: unknown integer arg size (%d)", + length); + break; + } + break; + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + /* decimal conversions */ + if (!decimal_conversion) + { + conversion_error = 1; + break; + } + if (length == -1) + { + length = sizeof (double); + } + switch (length) + { + case sizeof(double): + { + double arg_double = va_arg (args, double); + b->data = asnprintf(NULL, &dummy, buf, arg_double); + break; + } +#ifdef UNIQUE_FLOAT_TYPE_LONG_DOUBLE /* implies HAVE_LONG_DOUBLE */ + case sizeof(long double): + { + long double arg_long_double = va_arg (args, long double); + b->data = asnprintf(NULL, &dummy, buf, arg_long_double); + break; + } +#endif /* UNIQUE_FLOAT_TYPE_LONG_DOUBLE */ + default: + dellist(&pflist); + free(b); + error (1, 0, +"internal error: unknown floating point arg size (%d)", + length); + break; + } + break; + case 's': + switch (string_conversion) + { + case 1: + b->data = xstrdup (va_arg (args, char *)); + break; +#ifdef HAVE_WCHAR_T + case 2: + { + wchar_t *arg_wchar_t_string = va_arg (args, wchar_t *); + b->data = asnprintf (NULL, &dummy, buf, + arg_wchar_t_string); + break; + } +#endif /* HAVE_WCHAR_T */ + default: + conversion_error = 1; + break; + } + break; + case ',': + if (length != -1) + { + conversion_error = 1; + break; + } + b->data = va_arg (args, List *); + b->convproc = va_arg (args, CONVPROC_t); + b->closure = va_arg (args, void *); + break; + default: + conversion_error = 1; + break; + } + free (buf); + /* fail if we found an error or haven't found the end of the string */ + if (conversion_error || s[1]) + { + error (1, 0, +"internal error (format_cmdline): '%s' is not a valid conversion!!!", + conversion); + } + + + /* save our type - we really only care wheter it's a list type (',') + * or not from now on, but what the hell... + */ + b->conversion = *s; + + /* separate the user format string into parts and stuff our data into + * the pflist (once for each possible string - diverse keys can have + * duplicate data). + */ + q = pfmt; + while (*q) + { + struct cmdline_bindings *tb; + if (*q == '{') + { + s = q + 1; + while (*++q && *q != '}'); + r = q + 1; + } + else + { + s = q++; + r = q; + } + if (*r) + { + /* copy the data since we'll need it again */ + tb = xmalloc(sizeof(struct cmdline_bindings)); + if (b->conversion == ',') + { + tb->data = b->data; + } + else + { + tb->data = xstrdup(b->data); + } + tb->conversion = b->conversion; + tb->convproc = b->convproc; + tb->closure = b->closure; + } + else + { + /* we're done after this, so we don't need to copy the data */ + tb = b; + } + p = getnode(); + p->key = xmalloc((q - s) + 1); + strncpy (p->key, s, q - s); + p->key[q-s] = '\0'; + p->data = tb; + p->delproc = cmdline_bindings_hash_node_delete; + addnode(pflist,p); + } + } + + /* we're done with va_list */ + va_end(args); + + /* All formatted strings include a format character that resolves to the + * empty string by default, so put it in pflist. + */ + /* allocate space to save our data */ + b = xmalloc(sizeof(struct cmdline_bindings)); + b->conversion = 's'; + b->convproc = NULL; + b->closure = NULL; + b->data = xstrdup( "" ); + p = getnode(); + p->key = xstrdup( "n" ); + p->data = b; + p->delproc = cmdline_bindings_hash_node_delete; + addnode( pflist,p ); + + /* finally, read the user string and copy it into rargv as appropriate */ + /* user format strings look as follows: + * + * %% is a literal % + * \X, where X is any character = \X, (this is the escape you'd expect, but + * we are leaving the \ for an expected final pass which splits our + * output string into separate arguments + * + * %X means sub var "X" into location + * %{VWXYZ} means sub V,W,X,Y,Z into location as a single arg. The shell + * || would be to quote the comma separated arguments. Each list + * that V, W, X, Y, and Z represent attributes of will cause a new + * tuple to be inserted for each list item with a space between + * items. + * e.g."V W1,X1,Z1 W2,X2,Z2 W3,X3,Z3 Y1 Y2" where V is not a list + * variable, W,X,&Z are attributes of a list with 3 items and Y is an + * attribute of a second list with 2 items. + * %,{VWXYZ} means to separate the args. The previous example would produce + * V W1 X1 Z1 W2 X2 Z2 W3 X3 Z3 Y1 Y2, where each variable is now a + * separate, space delimited, arguments within a single argument. + * a%{XY}, where 'a' is a literal, still produces a single arg (a"X Y", in + * shell) + * a%1{XY}, where 'a' is a literal, splits the literal as it produces + * multiple args (a X Y). The rule is that each sub will produce a + * separate arg. Without a comma, attributes will still be grouped + * together & comma separated in what could be a single argument, + * but internal quotes, commas, and spaces are not excaped. + * + * clearing the variable oldway, passed into this function, causes the + * behavior of '1' and "," in the format string to reverse. + */ + + /* for convenience, use fmt as a temporary key buffer. + * for speed, attempt to realloc it as little as possible + */ + fmt = NULL; + flen = 0; + + /* buf = current argv entry being built + * length = current length of buf + * s = next char in source buffer to read + * d = next char location to write (in buf) + * inquotes = current quote char or NUL + */ + s = format; + d = buf = NULL; + length = 0; + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + + inquotes = '\0'; +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + subbedsomething = 0; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + while ((*d++ = *s) != '\0') + { + int list = 0; + switch (*s++) + { + case '\\': + /* the character after a \ goes unprocessed but leave the \ in + * the string so the function that splits this string into a + * command line later can deal with quotes properly + * + * ignore a NUL + */ + if (*s) + { + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + *d++ = *s++; + } + break; + case '\'': + case '"': + /* keep track of quotes so we can escape quote chars we sub in + * - the API is that a quoted format string will guarantee that + * it gets passed into the command as a single arg + */ + if (!inquotes) inquotes = s[-1]; + else if (s[-1] == inquotes) inquotes = '\0'; + break; + case '%': + if (*s == '%') + { + /* "%%" is a literal "%" */ + s++; + break; + } +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (oldway && subbedsomething) + { + /* the old method was to sub only the first format string */ + break; + } + /* initialize onearg each time we get a new format string */ + onearg = oldway ? 1 : 0; + subbedsomething = 1; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + d--; /* we're going to overwrite the '%' regardless + * of other factors... */ +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + /* detect '1' && ',' in the fmt string. */ + if (*s == '1') + { + onearg = 1; + s++; + if (!oldway) + { + /* FIXME - add FILE && LINE */ + error (0, 0, +"Using deprecated info format strings. Convert your scripts to use\n" +"the new argument format and remove '1's from your info file format strings."); + } + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + + /* parse the format string and sub in... */ + if (*s == '{') + { + list = 1; + s++; + } + /* q = fmt start + * r = fmt end + 1 + */ + q = fmt; + do + { + qoff = q - fmt; + expand_string (&fmt, &flen, qoff + 1); + q = fmt + qoff; + } while ((*q = *s++) && list && *q++ != '}'); + /* we will always copy one character, so, whether in list mode + * or not, if we just copied a '\0', then we hit the end of the + * string before we should have + */ + if (!s[-1]) + { + /* if we copied a NUL while processing a list, fail + * - we had an empty fmt string or didn't find a list + * terminator ('}') + */ + /* FIXME - this wants a file name and line number in a bad + * way. + */ + error(1, 0, +"unterminated format string encountered in command spec.\n" +"This error is likely to have been caused by an invalid line in a hook script\n" +"spec (see taginfo, loginfo, verifymsginfo, etc. in the Cederqvist). Most\n" +"likely the offending line would end with a '%%' character or contain a string\n" +"beginning \"%%{\" and no closing '}' before the end of the line."); + } + if (list) + { + q[-1] = '\0'; + } + else + { + /* We're not in a list, so we must have just copied a + * single character. Terminate the string. + */ + q++; + qoff = q - fmt; + expand_string (&fmt, &flen, qoff + 1); + q = fmt + qoff; + *q = '\0'; + } + /* fmt is now a pointer to a list of fmt chars, though the list + * could be a single element one + */ + q = fmt; +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + /* always add quotes in the deprecated onearg case - for + * backwards compatibility + */ + if (onearg) + { + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + *d++ = '"'; + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + /* + * for each character in the fmt string, + * + * all output will be separate quoted arguments (with + * internal quotes escaped) if the argument is in quotes + * unless the oldway variable is set, in which case the fmt + * statment will correspond to a single argument with + * internal space or comma delimited arguments + * + * see the "user format strings" section above for more info + */ + key[0] = *q; + if ((p = findnode (pflist, key)) != NULL) + { + b = p->data; + if (b->conversion == ',') + { + /* process the rest of the format string as a list */ + struct format_cmdline_walklist_closure c; + c.format = q; + c.buf = &buf; + c.length = &length; + c.d = &d; + c.quotes = inquotes; + c.closure = b->closure; +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + c.onearg = onearg; + c.firstpass = 1; + c.srepos = srepos; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + walklist(b->data, b->convproc, &c); + d--; /* back up one space. we know that ^ + always adds 1 extra */ + q += strlen(q); + } + else + { + /* got a flat item */ + char *outstr; + if (strlen(q) > 1) + { + error (1, 0, +"Multiple non-list variables are not allowed in a single format string."); + } +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (onearg) + { + outstr = b->data; + } + else /* !onearg */ + { +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + /* the *only* case possible without + * SUPPORT_OLD_INFO_FORMAT_STRINGS + * - !onearg */ + if (!inquotes) + { + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + *d++ = '"'; + } + outstr = cmdlineescape (inquotes ? inquotes : '"', b->data); +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + } /* onearg */ +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + doff = d - buf; + expand_string (&buf, &length, doff + strlen(outstr)); + d = buf + doff; + strncpy(d, outstr, strlen(outstr)); + d += strlen(outstr); +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (!onearg) + { + free(outstr); +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + if (!inquotes) + { + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + *d++ = '"'; + } +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + q++; + } + } +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + else if (onearg) + { + /* the old standard was to ignore unknown format + * characters (print the empty string), but also that + * any format character meant print srepos first + */ + q++; + doff = d - buf; + expand_string (&buf, &length, doff + strlen(srepos)); + d = buf + doff; + strncpy(d, srepos, strlen(srepos)); + d += strlen(srepos); + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + else /* no key */ + { + /* print an error message to the user + * FIXME - this should have a file and line number!!! */ + error (1, 0, +"Unknown format character in info file ('%s').\n" +"Info files are the hook files, verifymsg, taginfo, commitinfo, etc.", + q); + } +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + /* always add quotes in the deprecated onearg case - for + * backwards compatibility + */ + if (onearg) + { + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + *d++ = '"'; + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + break; + } + doff = d - buf; + expand_string (&buf, &length, doff + 1); + d = buf + doff; + } /* while (*d++ = *s) */ + if (fmt) free (fmt); + if (inquotes) + { + /* FIXME - we shouldn't need this - Parse_Info should be handling + * multiple lines... + */ + error (1, 0, "unterminated quote in format string: %s", format); + } + + dellist (&pflist); + return buf; +} + + + +/* Return non-zero iff FILENAME is absolute. + Trivial under Unix, but more complicated under other systems. */ +int +isabsolute (filename) + const char *filename; +{ + return ISABSOLUTE (filename); +} + + + +/* + * void cvs_trace(int level, const char *fmt, ...) + * + * Print tracing information to stderr on request. I haven't decided to + * actually use levels yet, but I did implement them as CVSNT did. + */ +void cvs_trace ( int level, const char *fmt, ... ) +{ + if(trace >= level) + { + va_list va; + + va_start( va, fmt ); +#ifdef SERVER_SUPPORT + fprintf(stderr,"%c -> ",server_active?'S':' '); +#else /* ! SERVER_SUPPORT */ + fprintf(stderr," -> "); +#endif + vfprintf(stderr, fmt, va); + fprintf(stderr,"\n"); + va_end(va); + } +} diff --git a/contrib/cvs-1.12.9/src/tag.c b/contrib/cvs-1.12.9/src/tag.c new file mode 100644 index 0000000000..aa78bc52e4 --- /dev/null +++ b/contrib/cvs-1.12.9/src/tag.c @@ -0,0 +1,1507 @@ +/* + * 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 source distribution. + * + * Tag and Rtag + * + * Add or delete a symbolic name to an RCS file, or a collection of RCS files. + * Tag uses the checked out revision in the current directory, rtag uses + * the modules database, if necessary. + */ + +#include "cvs.h" +#include "savecwd.h" + +static int rtag_proc (int argc, char **argv, char *xwhere, + char *mwhere, char *mfile, int shorten, + int local_specified, char *mname, char *msg); +static int check_fileproc (void *callerdat, struct file_info *finfo); +static int check_filesdoneproc (void *callerdat, int err, + const char *repos, const char *update_dir, + List *entries); +static int pretag_proc (const char *_repository, const char *_filter, + void *_closure); +static void masterlist_delproc (Node *_p); +static void tag_delproc (Node *_p); +static int pretag_list_to_args_proc (Node *_p, void *_closure); + +static Dtype tag_dirproc (void *callerdat, const char *dir, + const char *repos, const char *update_dir, + List *entries); +static int rtag_fileproc (void *callerdat, struct file_info *finfo); +static int rtag_delete (RCSNode *rcsfile); +static int tag_fileproc (void *callerdat, struct file_info *finfo); + +static char *numtag; /* specific revision to tag */ +static bool numtag_validated = false; +static char *date = NULL; +static char *symtag; /* tag to add or delete */ +static bool delete_flag; /* adding a tag by default */ +static bool branch_mode; /* make an automagic "branch" tag */ +static bool disturb_branch_tags = false;/* allow -F,-d to disturb branch tags */ +static bool force_tag_match = true; /* force tag to match by default */ +static bool force_tag_move; /* don't force tag to move by default */ +static bool check_uptodate; /* no uptodate-check by default */ +static bool attic_too; /* remove tag from Attic files */ +static bool is_rtag; + +struct tag_info +{ + Ctype status; + char *oldrev; + char *rev; + char *tag; + char *options; +}; + +struct master_lists +{ + List *tlist; +}; + +static List *mtlist; + +static const char rtag_opts[] = "+aBbdFflnQqRr:D:"; +static const char *const rtag_usage[] = +{ + "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n", + "\t-a\tClear tag from removed files that would not otherwise be tagged.\n", + "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", + "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n", + "\t-d\tDelete the given tag.\n", + "\t-F\tMove tag if it already exists.\n", + "\t-f\tForce a head revision match if tag/date not found.\n", + "\t-l\tLocal directory only, not recursive.\n", + "\t-n\tNo execution of 'tag program'.\n", + "\t-R\tProcess directories recursively.\n", + "\t-r rev\tExisting revision/tag.\n", + "\t-D\tExisting date.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +static const char tag_opts[] = "+BbcdFflQqRr:D:"; +static const char *const tag_usage[] = +{ + "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n", + "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", + "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n", + "\t-c\tCheck that working files are unmodified.\n", + "\t-d\tDelete the given tag.\n", + "\t-F\tMove tag if it already exists.\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-r rev\tExisting revision/tag.\n", + "\t-D\tExisting date.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + + + +int +cvstag (int argc, char **argv) +{ + bool local = false; /* recursive by default */ + int c; + int err = 0; + bool run_module_prog = true; + + is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0); + + if (argc == -1) + usage (is_rtag ? rtag_usage : tag_usage); + + optind = 0; + while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1) + { + switch (c) + { + case 'a': + attic_too = true; + break; + case 'b': + branch_mode = true; + break; + case 'B': + disturb_branch_tags = true; + break; + case 'c': + check_uptodate = true; + break; + case 'd': + delete_flag = true; + break; + case 'F': + force_tag_move = true; + break; + case 'f': + force_tag_match = false; + break; + case 'l': + local = true; + break; + case 'n': + run_module_prog = false; + break; + 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) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + cvs_cmd_name); + break; + case 'R': + local = false; + break; + case 'r': + numtag = optarg; + break; + case 'D': + if (date) + free (date); + date = Make_Date (optarg); + break; + case '?': + default: + usage (is_rtag ? rtag_usage : tag_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (argc < (is_rtag ? 2 : 1)) + usage (is_rtag ? rtag_usage : tag_usage); + symtag = argv[0]; + argc--; + argv++; + + if (date && numtag) + error (1, 0, "-r and -D options are mutually exclusive"); + if (delete_flag && branch_mode) + error (0, 0, "warning: -b ignored with -d options"); + RCS_check_tag (symtag); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (attic_too) + send_arg ("-a"); + if (branch_mode) + send_arg ("-b"); + if (disturb_branch_tags) + send_arg ("-B"); + if (check_uptodate) + send_arg ("-c"); + if (delete_flag) + send_arg ("-d"); + if (force_tag_move) + send_arg ("-F"); + if (!force_tag_match) + send_arg ("-f"); + if (local) + send_arg ("-l"); + if (!run_module_prog) + send_arg ("-n"); + + if (numtag) + option_with_arg ("-r", numtag); + if (date) + client_senddate (date); + + send_arg ("--"); + + send_arg (symtag); + + if (is_rtag) + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + send_to_server ("rtag\012", 0); + } + else + { + send_files (argc, argv, local, 0, + + /* I think the -c case is like "cvs status", in + which we really better be correct rather than + being fast; it is just too confusing otherwise. */ + check_uptodate ? 0 : SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server ("tag\012", 0); + } + + return get_responses_and_close (); + } +#endif + + if (is_rtag) + { + DBM *db; + int i; + db = open_module (); + for (i = 0; i < argc; i++) + { + /* XXX last arg should be repository, but doesn't make sense here */ + history_write ('T', (delete_flag ? "D" : (numtag ? numtag : + (date ? date : "A"))), symtag, argv[i], ""); + err += do_module (db, argv[i], TAG, + delete_flag ? "Untagging" : "Tagging", + rtag_proc, NULL, 0, local, run_module_prog, + 0, symtag); + } + close_module (db); + } + else + { + err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL, + NULL); + } + + return err; +} + + + +/* + * callback proc for doing the real work of tagging + */ +/* ARGSUSED */ +static int +rtag_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile, + int shorten, int local_specified, char *mname, char *msg) +{ + /* Begin section which is identical to patch_proc--should this + be abstracted out somehow? */ + char *myargv[2]; + int err = 0; + int which; + char *repository; + char *where; + +#ifdef HAVE_PRINTF_PTR + TRACE (TRACE_FUNCTION, + "rtag_proc (argc=%d, argv=%p, xwhere=%s,\n" + " mwhere=%s, mfile=%s, shorten=%d,\n" + " local_specified=%d, mname=%s, msg=%s)", + argc, (void *)argv, xwhere ? xwhere : "(null)", + mwhere ? mwhere : "(null)", mfile ? mfile : "(null)", + shorten, local_specified, + mname ? mname : "(null)", msg ? msg : "(null)" ); +#else + TRACE (TRACE_FUNCTION, + "rtag_proc (argc=%d, argv=%lx, xwhere=%s,\n" + " mwhere=%s, mfile=%s, shorten=%d,\n" + " local_specified=%d, mname=%s, msg=%s )", + argc, (unsigned long)argv, xwhere ? xwhere : "(null)", + mwhere ? mwhere : "(null)", mfile ? mfile : "(null)", + shorten, local_specified, + mname ? mname : "(null)", msg ? msg : "(null)" ); +#endif + + if (is_rtag) + { + repository = xmalloc (strlen (current_parsed_root->directory) + + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + + 2); + (void) sprintf (repository, "%s/%s", current_parsed_root->directory, + argv[0]); + where = xmalloc (strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile) + 1) + + 1); + (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; + + /* If the portion of the module is a path, put the dir part on + * REPOS. + */ + if ((cp = strrchr (mfile, '/')) != NULL) + { + *cp = '\0'; + (void) strcat (repository, "/"); + (void) strcat (repository, mfile); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + mfile = cp + 1; + } + + /* take care of the rest */ + path = xmalloc (strlen (repository) + strlen (mfile) + 5); + (void) sprintf (path, "%s/%s", repository, mfile); + if (isdir (path)) + { + /* directory means repository gets the dir tacked on */ + (void) strcpy (repository, path); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + } + else + { + myargv[0] = argv[0]; + myargv[1] = mfile; + argc = 2; + argv = myargv; + } + free (path); + } + + /* cd to the starting repository */ + if (CVS_CHDIR (repository) < 0) + { + error (0, errno, "cannot chdir to %s", repository); + free (repository); + return 1; + } + /* End section which is identical to patch_proc. */ + + if (delete_flag || attic_too || (force_tag_match && numtag)) + which = W_REPOS | W_ATTIC; + else + which = W_REPOS; + } + else + { + where = NULL; + which = W_LOCAL; + repository = ""; + } + + if (numtag != NULL && !numtag_validated) + { + tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0, + repository ); + numtag_validated = true; + } + + /* check to make sure they are authorized to tag all the + specified files in the repository */ + + mtlist = getlist(); + err = start_recursion (check_fileproc, check_filesdoneproc, + NULL, NULL, NULL, + argc - 1, argv + 1, local_specified, which, 0, + CVS_LOCK_READ, where, 1, repository); + + if (err) + { + error (1, 0, "correct the above errors first!"); + } + + /* 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). */ + + /* start the recursion processor */ + err = start_recursion + (is_rtag ? rtag_fileproc : tag_fileproc, + NULL, tag_dirproc, NULL, NULL, argc - 1, argv + 1, + local_specified, which, 0, CVS_LOCK_WRITE, where, 1, + repository); + dellist (&mtlist); + if (which & W_REPOS) free (repository); + if (where != NULL) + free (where); + return err; +} + + + +/* check file that is to be tagged */ +/* All we do here is add it to our list */ +static int +check_fileproc (void *callerdat, struct file_info *finfo) +{ + const char *xdir; + Node *p; + Vers_TS *vers; + List *tlist; + struct tag_info *ti; + int addit = 1; + + TRACE (TRACE_FUNCTION, "check_fileproc (%s, %s, %s)", + finfo->repository ? finfo->repository : "(null)", + finfo->fullname ? finfo->fullname : "(null)", + finfo->rcs ? (finfo->rcs->path ? finfo->rcs->path : "(null)") + : "NULL"); + + if (check_uptodate) + { + switch (Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0)) + { + case T_UPTODATE: + case T_CHECKOUT: + case T_PATCH: + case T_REMOVE_ENTRY: + break; + case T_UNKNOWN: + case T_CONFLICT: + case T_NEEDS_MERGE: + case T_MODIFIED: + case T_ADDED: + case T_REMOVED: + default: + error (0, 0, "%s is locally modified", finfo->fullname); + freevers_ts (&vers); + return 1; + } + } + else + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); + + if (finfo->update_dir[0] == '\0') + xdir = "."; + else + xdir = finfo->update_dir; + if ((p = findnode (mtlist, xdir)) != NULL) + { + tlist = ((struct master_lists *) p->data)->tlist; + } + else + { + struct master_lists *ml; + + tlist = getlist (); + p = getnode (); + p->key = xstrdup (xdir); + p->type = UPDATE; + ml = xmalloc (sizeof (struct master_lists)); + ml->tlist = tlist; + p->data = ml; + p->delproc = masterlist_delproc; + (void) addnode (mtlist, p); + } + /* do tlist */ + p = getnode (); + p->key = xstrdup (finfo->file); + p->type = UPDATE; + p->delproc = tag_delproc; + if (vers->srcfile == NULL) + { + if (!really_quiet) + error (0, 0, "nothing known about %s", finfo->file); + freevers_ts (&vers); + freenode (p); + return 1; + } + + /* Here we duplicate the calculation in tag_fileproc about which + version we are going to tag. There probably are some subtle races + (e.g. numtag is "foo" which gets moved between here and + tag_fileproc). */ + p->data = ti = xmalloc (sizeof (struct tag_info)); + if (!is_rtag && numtag == NULL && date == NULL) + ti->rev = xstrdup (vers->vn_user); + else + ti->rev = RCS_getversion (vers->srcfile, numtag, date, + force_tag_match, NULL); + + if (ti->rev != NULL) + { + ti->oldrev = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL); + + if (ti->oldrev == NULL) + { + if (delete_flag) + { + /* Deleting a tag which did not exist is a noop and + should not be logged. */ + addit = 0; + } + } + else if (delete_flag) + { + free (ti->rev); +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + /* a hack since %v used to mean old or new rev */ + ti->rev = xstrdup (ti->oldrev); +#else /* SUPPORT_OLD_INFO_FMT_STRINGS */ + ti->rev = NULL; +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + } + else if (strcmp(ti->oldrev, p->data) == 0) + addit = 0; + else if (!force_tag_move) + addit = 0; + } + else + addit = 0; + if (!addit) + { + free(p->data); + p->data = NULL; + } + freevers_ts (&vers); + (void)addnode (tlist, p); + return 0; +} + + + +struct pretag_proc_data { + List *tlist; + bool delete_flag; + bool force_tag_move; + char *symtag; +}; + +static int +check_filesdoneproc (void *callerdat, int err, const char *repos, + const char *update_dir, List *entries) +{ + int n; + Node *p; + List *tlist; + struct pretag_proc_data ppd; + + p = findnode(mtlist, update_dir); + if (p != NULL) + { + tlist = ((struct master_lists *) p->data)->tlist; + } + else + { + tlist = (List *) NULL; + } + if ((tlist == NULL) || (tlist->list->next == tlist->list)) + { + return (err); + } + + ppd.tlist = tlist; + ppd.delete_flag = delete_flag; + ppd.force_tag_move = force_tag_move; + ppd.symtag = symtag; + if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, PIOPT_ALL, &ppd)) > 0) + { + error (0, 0, "Pre-tag check failed"); + err += n; + } + return err; +} + + + +/* + * called from Parse_Info, this routine processes a line that came out + * of a taginfo file and turns it into a command and executes it. + * + * RETURNS + * the absolute value of the return value of run_exec, which may or + * may not be the return value of the child process. this is + * contrained to return positive values because Parse_Info is adding up + * return values and testing for non-zeroness to signify one or more + * of its callbacks having returned an error. + */ +static int +pretag_proc (const char *repository, const char *filter, void *closure) +{ + char *newfilter = NULL; + char *cmdline; + const char *srepos = Short_Repository (repository); + struct pretag_proc_data *ppd = (struct pretag_proc_data *)closure; + +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + if (!strchr(filter, '%')) + { + error(0,0, + "warning: taginfo line contains no format strings:\n" + " \"%s\"\n" + "Filling in old defaults ('%%t %%o %%p %%{sv}'), but please be aware that this\n" + "usage is deprecated.", filter); + newfilter = xmalloc (strlen(filter) + 16); + strcpy (newfilter, filter); + strcat (newfilter, " %t %o %p %{sv}"); + filter = newfilter; + } +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + + /* %t = tag being added/moved/removed + * %o = operation = "add" | "mov" | "del" + * %b = branch mode = "?" (delete ops - unknown) | "T" (branch) | "N" (not branch) + * %p = path from $CVSROOT + * %r = path from root + * %{sVv} = attribute list = file name, old version tag will be deleted from, + * new version tag will be added to (or deleted from until + * SUPPORT_OLD_INFO_FMT_STRINGS is undefined) + */ + cmdline = format_cmdline( +#ifdef SUPPORT_OLD_INFO_FMT_STRINGS + 0, srepos, +#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ + filter, + "t", "s", ppd->symtag, + "o", "s", ppd->delete_flag ? "del" : + ppd->force_tag_move ? "mov" : "add", + "b", "c", delete_flag ? '?' : branch_mode ? 'T' : 'N', + "p", "s", srepos, + "r", "s", current_parsed_root->directory, + "sVv", ",", ppd->tlist, pretag_list_to_args_proc, (void *) NULL, + (char *)NULL + ); + + if (newfilter) free (newfilter); + + if (!cmdline || !strlen (cmdline)) + { + if (cmdline) free (cmdline); + error(0, 0, "pretag proc resolved to the empty string!"); + return 1; + } + + run_setup (cmdline); + + /* FIXME - the old code used to run the following here: + * + * if (!isfile(s)) + * { + * error (0, errno, "cannot find pre-tag filter '%s'", s); + * free(s); + * return (1); + * } + * + * not sure this is really necessary. it might give a little finer grained + * error than letting the execution attempt fail but i'm not sure. in any + * case it should be easy enough to add a function in run.c to test its + * first arg for fileness & executability. + */ + + free (cmdline); + return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)); +} + +static void +masterlist_delproc(Node *p) +{ + struct master_lists *ml = p->data; + + dellist(&ml->tlist); + free(ml); + return; +} + +static void +tag_delproc(Node *p) +{ + struct tag_info *ti; + if (p->data != NULL) + { + ti = (struct tag_info *) p->data; + if (ti->oldrev) free (ti->oldrev); + if (ti->rev) free (ti->rev); + free(p->data); + p->data = NULL; + } + return; +} + +/* to be passed into walklist with a list of tags + * p->key = tagname + * p->data = struct tag_info * + * p->data->oldrev = rev tag will be deleted from + * p->data->rev = rev tag will be added to + * + * closure will be a struct format_cmdline_walklist_closure + * where closure is undefined + */ +static int +pretag_list_to_args_proc(Node *p, void *closure) +{ + struct tag_info *taginfo = (struct tag_info *)p->data; + struct format_cmdline_walklist_closure *c = + (struct format_cmdline_walklist_closure *)closure; + char *arg = NULL; + const char *f; + char *d; + size_t doff; + + if (p->data == NULL) return 1; + + f = c->format; + d = *c->d; + /* foreach requested attribute */ + while (*f) + { + switch (*f++) + { + case 's': + arg = p->key; + break; + case 'v': + arg = taginfo->rev ? taginfo->rev : "NONE"; + break; + case 'V': + arg = taginfo->oldrev ? taginfo->oldrev : "NONE"; + break; + default: + error(1,0, + "Unknown format character or not a list attribute: %c", + f[-1]); + break; + } + /* copy the attribute into an argument */ + if (c->quotes) + { + arg = cmdlineescape (c->quotes, arg); + } + else + { + arg = cmdlinequote ('"', arg); + } + + doff = d - *c->buf; + expand_string (c->buf, c->length, doff + strlen (arg)); + d = *c->buf + doff; + strncpy (d, arg, strlen (arg)); + d += strlen (arg); + + free (arg); + + /* and always put the extra space on. we'll have to back up a char when we're + * done, but that seems most efficient + */ + doff = d - *c->buf; + expand_string (c->buf, c->length, doff + 1); + d = *c->buf + doff; + *d++ = ' '; + } + /* correct our original pointer into the buff */ + *c->d = d; + return 0; +} + + +/* + * Called to rtag a particular file, as appropriate with the options that were + * set above. + */ +/* ARGSUSED */ +static int +rtag_fileproc (void *callerdat, struct file_info *finfo) +{ + RCSNode *rcsfile; + char *version, *rev; + int retcode = 0; + + /* find the parsed RCS data */ + if ((rcsfile = finfo->rcs) == NULL) + return 1; + + /* + * For tagging an RCS file which is a symbolic link, you'd best be + * running with RCS 5.6, since it knows how to handle symbolic links + * correctly without breaking your link! + */ + + if (delete_flag) + return rtag_delete (rcsfile); + + /* + * If we get here, we are adding a tag. But, if -a was specified, we + * need to check to see if a -r or -D option was specified. If neither + * was specified and the file is in the Attic, remove the tag. + */ + if (attic_too && (!numtag && !date)) + { + if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC)) + return rtag_delete (rcsfile); + } + + version = RCS_getversion (rcsfile, numtag, date, force_tag_match, NULL); + if (version == NULL) + { + /* If -a specified, clean up any old tags */ + if (attic_too) + (void)rtag_delete (rcsfile); + + if (!quiet && !force_tag_match) + { + error (0, 0, "cannot find tag `%s' in `%s'", + numtag ? numtag : "head", rcsfile->path); + return 1; + } + return 0; + } + if (numtag + && isdigit ((unsigned char)*numtag) + && strcmp (numtag, version) != 0) + { + + /* + * We didn't find a match for the numeric tag that was specified, but + * that's OK. just pass the numeric tag on to rcs, to be tagged as + * specified. Could get here if one tried to tag "1.1.1" and there + * was a 1.1.1 branch with some head revision. In this case, we want + * the tag to reference "1.1.1" and not the revision at the head of + * the branch. Use a symbolic tag for that. + */ + rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag; + retcode = RCS_settag(rcsfile, symtag, numtag); + if (retcode == 0) + RCS_rewrite (rcsfile, NULL, NULL); + } + else + { + char *oversion; + + /* + * As an enhancement for the case where a tag is being re-applied to + * a large body of a module, make one extra call to RCS_getversion to + * see if the tag is already set in the RCS file. If so, check to + * see if it needs to be moved. If not, do nothing. This will + * likely save a lot of time when simply moving the tag to the + * "current" head revisions of a module -- which I have found to be a + * typical tagging operation. + */ + rev = branch_mode ? RCS_magicrev (rcsfile, version) : version; + oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, + (int *) NULL); + if (oversion != NULL) + { + int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); + + /* + * if versions the same and neither old or new are branches don't + * have to do anything + */ + if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) + { + free (oversion); + free (version); + return (0); + } + + 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); + free (oversion); + free (version); + if (branch_mode) free (rev); + return 0; + } + else /* force_tag_move is set and... */ + if ((isbranch && !disturb_branch_tags) || + (!isbranch && disturb_branch_tags)) + { + error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.", + finfo->fullname, + isbranch ? "branch" : "non-branch", + symtag, oversion, rev, + isbranch ? "" : " due to `-B' option"); + if (branch_mode) free(rev); + free (oversion); + free (version); + return 0; + } + free (oversion); + } + retcode = RCS_settag(rcsfile, symtag, rev); + if (retcode == 0) + RCS_rewrite (rcsfile, NULL, NULL); + } + + if (retcode != 0) + { + error (1, retcode == -1 ? errno : 0, + "failed to set tag `%s' to revision `%s' in `%s'", + symtag, rev, rcsfile->path); + if (branch_mode) + free (rev); + free (version); + return 1; + } + if (branch_mode) + free (rev); + free (version); + return 0; +} + + + +/* + * If -d is specified, "force_tag_match" is set, so that this call to + * RCS_getversion() will return a NULL version string if the symbolic + * tag does not exist in the RCS file. + * + * If the -r flag was used, numtag is set, and we only delete the + * symtag from files that have numtag. + * + * This is done here because it's MUCH faster than just blindly calling + * "rcs" to remove the tag... trust me. + */ +static int +rtag_delete (RCSNode *rcsfile) +{ + char *version; + int retcode, isbranch; + + if (numtag) + { + version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, + (int *) NULL); + if (version == NULL) + return (0); + free (version); + } + + version = RCS_getversion (rcsfile, symtag, NULL, 1, NULL); + if (version == NULL) + return 0; + free (version); + + + isbranch = RCS_nodeisbranch (rcsfile, symtag); + if ((isbranch && !disturb_branch_tags) || + (!isbranch && disturb_branch_tags)) + { + if (!quiet) + error (0, 0, + "Not removing %s tag `%s' from `%s'%s.", + isbranch ? "branch" : "non-branch", + symtag, rcsfile->path, + isbranch ? "" : " due to `-B' option"); + return 1; + } + + if ((retcode = RCS_deltag(rcsfile, symtag)) != 0) + { + if (!quiet) + error (0, retcode == -1 ? errno : 0, + "failed to remove tag `%s' from `%s'", symtag, + rcsfile->path); + return 1; + } + RCS_rewrite (rcsfile, NULL, NULL); + return 0; +} + + + +/* + * Called to tag a particular file (the currently checked out version is + * tagged with the specified tag - or the specified tag is deleted). + */ +/* ARGSUSED */ +static int +tag_fileproc (void *callerdat, struct file_info *finfo) +{ + char *version, *oversion; + char *nversion = NULL; + char *rev; + Vers_TS *vers; + int retcode = 0; + int retval = 0; + + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); + + if ((numtag != NULL) || (date != NULL)) + { + nversion = RCS_getversion(vers->srcfile, + numtag, + date, + force_tag_match, + NULL); + if (nversion == NULL) + goto free_vars_and_return; + } + if (delete_flag) + { + + int isbranch; + /* + * If -d is specified, "force_tag_match" is set, so that this call to + * RCS_getversion() will return a NULL version string if the symbolic + * tag does not exist in the RCS file. + * + * This is done here because it's MUCH faster than just blindly calling + * "rcs" to remove the tag... trust me. + */ + + version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); + if (version == NULL || vers->srcfile == NULL) + goto free_vars_and_return; + + free (version); + + isbranch = RCS_nodeisbranch (finfo->rcs, symtag); + if ((isbranch && !disturb_branch_tags) || + (!isbranch && disturb_branch_tags)) + { + if (!quiet) + error(0, 0, + "Not removing %s tag `%s' from `%s'%s.", + isbranch ? "branch" : "non-branch", + symtag, vers->srcfile->path, + isbranch ? "" : " due to `-B' option"); + retval = 1; + goto free_vars_and_return; + } + + if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0) + { + if (!quiet) + error (0, retcode == -1 ? errno : 0, + "failed to remove tag %s from %s", symtag, + vers->srcfile->path); + retval = 1; + goto free_vars_and_return; + } + RCS_rewrite (vers->srcfile, NULL, NULL); + + /* warm fuzzies */ + if (!really_quiet) + { + cvs_output ("D ", 2); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); + } + + goto free_vars_and_return; + } + + /* + * If we are adding a tag, we need to know which version we have checked + * out and we'll tag that version. + */ + if (nversion == NULL) + { + version = vers->vn_user; + } + else + { + version = nversion; + } + if (version == NULL) + { + goto free_vars_and_return; + } + else if (strcmp (version, "0") == 0) + { + if (!quiet) + error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file); + goto free_vars_and_return; + } + else if (version[0] == '-') + { + if (!quiet) + error (0, 0, "skipping removed but un-commited file `%s'", finfo->file); + goto free_vars_and_return; + } + else if (vers->srcfile == NULL) + { + if (!quiet) + error (0, 0, "cannot find revision control file for `%s'", finfo->file); + goto free_vars_and_return; + } + + /* + * As an enhancement for the case where a tag is being re-applied to a + * large number of files, make one extra call to RCS_getversion to see + * if the tag is already set in the RCS file. If so, check to see if it + * needs to be moved. If not, do nothing. This will likely save a lot of + * time when simply moving the tag to the "current" head revisions of a + * module -- which I have found to be a typical tagging operation. + */ + rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); + if (oversion != NULL) + { + int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); + + /* + * if versions the same and neither old or new are branches don't have + * to do anything + */ + if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) + { + free (oversion); + if (branch_mode) + free (rev); + goto free_vars_and_return; + } + + if (!force_tag_move) + { + /* we're NOT going to move the tag */ + 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); + if (branch_mode) + free (rev); + goto free_vars_and_return; + } + else /* force_tag_move == 1 and... */ + if ((isbranch && !disturb_branch_tags) || + (!isbranch && disturb_branch_tags)) + { + error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.", + finfo->fullname, + isbranch ? "branch" : "non-branch", + symtag, oversion, rev, + isbranch ? "" : " due to `-B' option"); + free (oversion); + if (branch_mode) + free (rev); + goto free_vars_and_return; + } + free (oversion); + } + + 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", + symtag, rev, vers->srcfile->path); + if (branch_mode) + free (rev); + retval = 1; + goto free_vars_and_return; + } + if (branch_mode) + free (rev); + RCS_rewrite (vers->srcfile, NULL, NULL); + + /* more warm fuzzies */ + if (!really_quiet) + { + cvs_output ("T ", 2); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); + } + + free_vars_and_return: + if (nversion != NULL) + free (nversion); + freevers_ts (&vers); + return (retval); +} + + + +/* + * Print a warm fuzzy message + */ +/* ARGSUSED */ +static Dtype +tag_dirproc (void *callerdat, const char *dir, const char *repos, + const 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 (!quiet) + error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", + update_dir); + return R_PROCESS; +} + + + +/* Code relating to the val-tags file. Note that this file has no way + of knowing when a tag has been deleted. The problem is that there + is no way of knowing whether a tag still exists somewhere, when we + delete it some places. Using per-directory val-tags files (in + CVSREP) might be better, but that might slow down the process of + verifying that a tag is correct (maybe not, for the likely cases, + if carefully done), and/or be harder to implement correctly. */ + +struct val_args { + char *name; + int found; +}; + +static int val_fileproc (void *callerdat, struct file_info *finfo); + +static int +val_fileproc (void *callerdat, struct file_info *finfo) +{ + RCSNode *rcsdata; + struct val_args *args = (struct val_args *)callerdat; + char *tag; + + if ((rcsdata = finfo->rcs) == NULL) + /* Not sure this can happen, after all we passed only + W_REPOS | W_ATTIC. */ + return 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. */ + args->found = 1; + free (tag); + } + return 0; +} + + + +static Dtype +val_direntproc (void *callerdat, const char *dir, const char *repository, + const 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 + files in a directory which does not exist yet, but which is + about to be created. */ + if (isdir (dir)) + return R_PROCESS; + return R_SKIP_ALL; +} + +/* Check to see whether NAME is a valid tag. If so, return. If not + print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify + which files we will be operating on. + + REPOSITORY is the repository if we need to cd into it, or NULL if + we are already there, or "" if we should do a W_LOCAL recursion. + Sorry for three cases, but the "" case is needed in case the + working directories come from diverse parts of the repository, the + NULL case avoids an unneccesary chdir, and the non-NULL, non-"" + case is needed for checkout, where we don't want to chdir if the + tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any + local directory. */ +void +tag_check_valid (char *name, int argc, char **argv, int local, int aflag, + char *repository) +{ + DBM *db; + char *valtags_filename; + int nowrite = 0; + datum mytag; + struct val_args the_val_args; + struct saved_cwd cwd; + int which; + +#ifdef HAVE_PRINTF_PTR + TRACE ( TRACE_FUNCTION, + "tag_check_valid ( name=%s, argc=%d, argv=%p, local=%d,\n" + " aflag=%d, repository=%s )", + name ? name : "(name)", argc, (void *)argv, local, aflag, + repository ? repository : "(null)" ); +#else + TRACE ( TRACE_FUNCTION, + "tag_check_valid ( name=%s, argc=%d, argv=%lx, local=%d,\n" + " aflag=%d, repository=%s )", + name ? name : "(name)", argc, (unsigned long)argv, local, aflag, + repository ? repository : "(null)" ); +#endif + + /* Numeric tags require only a syntactic check. */ + if (isdigit ((unsigned char) name[0])) + { + char *p; + for (p = name; *p != '\0'; ++p) + { + if (!(isdigit ((unsigned char) *p) || *p == '.')) + error (1, 0, "\ +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; + + /* FIXME: This routine doesn't seem to do any locking whatsoever + (and it is called from places which don't have locks in place). + If two processes try to write val-tags at the same time, it would + seem like we are in trouble. */ + + mytag.dptr = name; + mytag.dsize = strlen (name); + + valtags_filename = xmalloc (strlen (current_parsed_root->directory) + + sizeof CVSROOTADM + + sizeof CVSROOTADM_VALTAGS + 3); + sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_VALTAGS); + db = dbm_open (valtags_filename, O_RDWR, 0666); + if (db == NULL) + { + if (!existence_error (errno)) + { + error (0, errno, "warning: cannot open %s read/write", + valtags_filename); + db = dbm_open (valtags_filename, O_RDONLY, 0666); + if (db != NULL) + nowrite = 1; + else if (!existence_error (errno)) + error (1, errno, "cannot read %s", valtags_filename); + } + /* If the file merely fails to exist, we just keep going and create + it later if need be. */ + } + if (db != NULL) + { + datum val; + + val = dbm_fetch (db, mytag); + if (val.dptr != NULL) + { + /* Found. The tag is valid. */ + dbm_close (db); + free (valtags_filename); + return; + } + /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */ + } + + /* 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. + + 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; + + which = W_REPOS | W_ATTIC; + + if (repository == NULL || repository[0] == '\0') + which |= W_LOCAL; + else + { + if (save_cwd (&cwd)) + exit (EXIT_FAILURE); + if (CVS_CHDIR (repository) < 0) + error (1, errno, "cannot change to %s directory", repository); + } + + start_recursion + (val_fileproc, NULL, val_direntproc, NULL, + &the_val_args, argc, argv, local, which, aflag, + CVS_LOCK_READ, NULL, 1, repository); + if (repository != NULL && repository[0] != '\0') + { + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + } + + if (!the_val_args.found) + error (1, 0, "no such tag %s", name); + else + { + /* The tags is valid but not mentioned in val-tags. Add it. */ + datum value; + + if (noexec || nowrite) + { + if (db != NULL) + dbm_close (db); + free (valtags_filename); + return; + } + + if (db == NULL) + { + mode_t omask; + omask = umask (cvsumask); + db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); + (void)umask (omask); + + if (db == NULL) + { + error (0, errno, "warning: cannot create %s", valtags_filename); + free (valtags_filename); + return; + } + } + value.dptr = "y"; + value.dsize = 1; + if (dbm_store (db, mytag, value, DBM_REPLACE) < 0) + error (0, errno, "cannot store %s into %s", name, + valtags_filename); + dbm_close (db); + } + 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 (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 ((unsigned char) join_tag[0])) + error (1, 0, + "Numeric join tag %s may not contain a date specifier", + join_tag); + + *s = '\0'; + /* hmmm... I think it makes sense to allow -j:, but + * for now this fixes a bug where CVS just spins and spins (I + * think in the RCS code) looking for a zero length tag. + */ + if (!*c) + error (1, 0, + "argument to join may not contain a date specifier without a tag"); + } + + tag_check_valid (c, argc, argv, local, aflag, repository); + + free (c); +} diff --git a/contrib/cvs-1.12.9/src/update.c b/contrib/cvs-1.12.9/src/update.c new file mode 100644 index 0000000000..74f2b4079f --- /dev/null +++ b/contrib/cvs-1.12.9/src/update.c @@ -0,0 +1,2953 @@ +/* + * 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 source distribution. + * + * "update" updates the version in the present directory with respect to the RCS + * repository. The present version must have been created by "checkout". The + * user can keep up-to-date by calling "update" whenever he feels like it. + * + * The present version can be committed by "commit", but this keeps the version + * in tact. + * + * Arguments following the options are taken to be file names to be updated, + * rather than updating the entire directory. + * + * Modified or non-existent RCS files are checked out and reported as U + * + * + * Modified user files are reported as M . If both the RCS file and + * the user file have been modified, the user file is replaced by the result + * of rcsmerge, and a backup file is written for the user in .#file.version. + * If this throws up irreconcilable differences, the file is reported as C + * , and as M otherwise. + * + * Files added but not yet committed are reported as A . Files + * removed but not yet committed are reported as R . + * + * If the current directory contains subdirectories that hold concurrent + * versions, these are updated too. If the -d option was specified, new + * directories added to the repository are automatically created and updated + * as well. + */ + +#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" +#include "buffer.h" +#include "hardlink.h" + +static int checkout_file (struct file_info *finfo, Vers_TS *vers_ts, + int adding, int merging, int update_server); +#ifdef SERVER_SUPPORT +static void checkout_to_buffer (void *, const char *, size_t); +static int patch_file (struct file_info *finfo, + Vers_TS *vers_ts, + int *docheckout, struct stat *file_info, + unsigned char *checksum); +static void patch_file_write (void *, const char *, size_t); +#endif +static int merge_file (struct file_info *finfo, Vers_TS *vers); +static int scratch_file (struct file_info *finfo, Vers_TS *vers); +static Dtype update_dirent_proc (void *callerdat, const char *dir, + const char *repository, + const char *update_dir, + List *entries); +static int update_dirleave_proc (void *callerdat, const char *dir, + int err, const char *update_dir, + List *entries); +static int update_fileproc (void *callerdat, struct file_info *); +static int update_filesdone_proc (void *callerdat, int err, + const char *repository, + const char *update_dir, List *entries); +#ifdef PRESERVE_PERMISSIONS_SUPPORT +static int get_linkinfo_proc( void *_callerdat, struct _finfo * ); +#endif +static void join_file (struct file_info *finfo, Vers_TS *vers_ts); + +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. warned is nonzero if we've already warned the user that the + tag occurs as both a revision tag and a branch tag. */ +static int rewrite_tag; +static int nonbranch; +static int warned; + +/* If we set the tag or date for a subdirectory, we use this to undo + the setting. See update_dirent_proc. */ +static char *tag_update_dir; + +static char *join_rev1, *date_rev1; +static char *join_rev2, *date_rev2; +static int aflag = 0; +static int toss_local_changes = 0; +static int force_tag_match = 1; +static int update_build_dirs = 0; +static int update_prune_dirs = 0; +static int pipeout = 0; +static int dotemplate = 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; +static const char *const update_usage[] = +{ + "Usage: %s %s [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev]\n", + " [-I ign] [-W spec] [files...]\n", + "\t-A\tReset any sticky tags/date/kopts.\n", + "\t-P\tPrune empty directories.\n", + "\t-C\tOverwrite locally modified files with clean repository copies.\n", + "\t-d\tBuild directories, like checkout does.\n", + "\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 (avoids stickiness).\n", + "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\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", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + + + +/* + * update is the argv,argc based front end for arg parsing + */ +int +update (int argc, char **argv) +{ + int c, err; + int local = 0; /* recursive by default */ + int which; /* where to look for files and dirs */ + + if (argc == -1) + usage (update_usage); + + ign_setup (); + wrap_setup (); + + /* parse the args */ + optind = 0; + while ((c = getopt (argc, argv, "+ApCPflRQqduk:r:D:j:I:W:")) != -1) + { + switch (c) + { + case 'A': + aflag = 1; + break; + case 'C': + toss_local_changes = 1; + break; + case 'I': + ign_add (optarg, 0); + break; + case 'W': + wrap_add (optarg, 0); + break; + case 'k': + if (options) + free (options); + options = RCS_check_kflag (optarg); + break; + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + 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) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + cvs_cmd_name); + break; + case 'd': + update_build_dirs = 1; + break; + case 'f': + force_tag_match = 0; + break; + case 'r': + tag = optarg; + break; + case 'D': + date = Make_Date (optarg); + break; + case 'P': + update_prune_dirs = 1; + break; + case 'p': + pipeout = 1; + noexec = 1; /* so no locks will be created */ + break; + case 'j': + if (join_rev2) + error (1, 0, "only two -j options can be specified"); + if (join_rev1) + join_rev2 = optarg; + else + join_rev1 = optarg; + break; + case 'u': +#ifdef SERVER_SUPPORT + if (server_active) + { + patches = 1; + rcs_diff_patches = server_use_rcs_diff (); + } + else +#endif + usage (update_usage); + break; + case '?': + default: + usage (update_usage); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + 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; + + start_server (); + + if (local) + send_arg("-l"); + if (update_build_dirs) + send_arg("-d"); + if (pipeout) + send_arg("-p"); + if (!force_tag_match) + send_arg("-f"); + if (aflag) + send_arg("-A"); + if (toss_local_changes) + send_arg("-C"); + if (update_prune_dirs) + 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 (failed_patches_count == 0) + { + unsigned int flags = 0; + + /* 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. + + We don't send -u if failed_patches != NULL, so that the + server doesn't try to send patches which will just fail + again. At least currently, the client also clobbers the + file and tells the server it is lost, which also will get + a full file instead of a patch, but it seems clean to omit + -u. */ + if (supported_request ("update-patches")) + send_arg ("-u"); + + send_arg ("--"); + + if (update_build_dirs) + flags |= SEND_BUILD_DIRS; + + if (toss_local_changes) { + flags |= SEND_NO_CONTENTS; + flags |= BACKUP_MODIFIED_FILES; + } + + /* If noexec, probably could be setting SEND_NO_CONTENTS. + Same caveats as for "cvs status" apply. */ + + send_files (argc, argv, local, aflag, flags); + send_file_names (argc, argv, SEND_EXPAND_WILD); + } + else + { + int i; + + (void) printf ("%s client: refetching unpatchable files\n", + program_name); + + if (toplevel_wd != NULL + && CVS_CHDIR (toplevel_wd) < 0) + { + error (1, errno, "could not chdir to %s", toplevel_wd); + } + + send_arg ("--"); + + for (i = 0; i < failed_patches_count; i++) + if (unlink_file (failed_patches[i]) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", + failed_patches[i]); + send_files (failed_patches_count, failed_patches, local, + aflag, update_build_dirs ? SEND_BUILD_DIRS : 0); + send_file_names (failed_patches_count, failed_patches, 0); + free_names (&failed_patches_count, failed_patches); + } + + send_to_server ("update\012", 0); + + status = get_responses_and_close (); + + /* 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_count == 0 || pass > 1)) + { + if (failed_patches_count > 0) + free_names (&failed_patches_count, failed_patches); + return status; + } + + ++pass; + } while (failed_patches_count > 0); + + return 0; + } +#endif + + if (tag != NULL) + tag_check_valid (tag, argc, argv, local, aflag, ""); + 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 + * as we go, we make sure there is no static entries file and write the + * tag file as appropriate + */ + if (argc <= 0 && !pipeout) + { + if (update_build_dirs) + { + if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno)) + error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT); +#ifdef SERVER_SUPPORT + if (server_active) + { + char *repos = Name_Repository (NULL, NULL); + server_clear_entstat (".", repos); + free (repos); + } +#endif + } + + /* keep the CVS/Tag file current with the specified arguments */ + if (aflag || tag || date) + { + char *repos = Name_Repository (NULL, NULL); + WriteTag (NULL, tag, date, 0, ".", repos); + free (repos); + rewrite_tag = 1; + nonbranch = -1; + warned = 0; + } + } + + /* look for files/dirs locally and in the repository */ + which = W_LOCAL | W_REPOS; + + /* look in the attic too if a tag or date is specified */ + if (tag != NULL || date != NULL || joining()) + which |= W_ATTIC; + + /* call the command line interface */ + err = do_update (argc, argv, options, tag, date, force_tag_match, + local, update_build_dirs, aflag, update_prune_dirs, + pipeout, which, join_rev1, join_rev2, NULL, 1, NULL); + + /* free the space Make_Date allocated if necessary */ + if (date != NULL) + free (date); + + return err; +} + + + +/* + * Command line interface to update (used by checkout) + * + * repository = cvsroot->repository + update_dir. This is necessary for + * checkout so that start_recursion can determine our repository. In the + * update case, start_recursion can use the CVS/Root & CVS/Repository file + * to determine this value. + */ +int +do_update (int argc, char **argv, char *xoptions, char *xtag, char *xdate, + int xforce, int local, int xbuild, int xaflag, int xprune, + int xpipeout, int which, char *xjoin_rev1, char *xjoin_rev2, + char *preload_update_dir, int xdotemplate, char *repository) +{ + int err = 0; + char *cp; + + /* fill in the statics */ + options = xoptions; + tag = xtag; + date = xdate; + force_tag_match = xforce; + update_build_dirs = xbuild; + aflag = xaflag; + update_prune_dirs = xprune; + pipeout = xpipeout; + dotemplate = xdotemplate; + + /* setup the join support */ + join_rev1 = xjoin_rev1; + join_rev2 = xjoin_rev2; + if (join_rev1 && (cp = strchr (join_rev1, ':')) != NULL) + { + *cp++ = '\0'; + date_rev1 = Make_Date (cp); + } + else + date_rev1 = NULL; + if (join_rev2 && (cp = strchr (join_rev2, ':')) != NULL) + { + *cp++ = '\0'; + date_rev2 = Make_Date (cp); + } + else + date_rev2 = NULL; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms) + { + /* We need to do an extra recursion, bleah. It's to make sure + that we know as much as possible about file linkage. */ + hardlist = getlist(); + working_dir = xgetwd(); /* save top-level working dir */ + + /* FIXME-twp: the arguments to start_recursion make me dizzy. This + function call was copied from the update_fileproc call that + follows it; someone should make sure that I did it right. */ + err = start_recursion + (get_linkinfo_proc, NULL, NULL, NULL, NULL, + argc, argv, local, which, aflag, CVS_LOCK_READ, + preload_update_dir, 1, NULL); + if (err) + return err; + + /* FIXME-twp: at this point we should walk the hardlist + and update the `links' field of each hardlink_info struct + to list the files that are linked on dist. That would make + it easier & more efficient to compare the disk linkage with + the repository linkage (a simple strcmp). */ + } +#endif + + /* call the recursion processor */ + err = start_recursion (update_fileproc, update_filesdone_proc, + update_dirent_proc, update_dirleave_proc, NULL, + argc, argv, local, which, aflag, CVS_LOCK_READ, + preload_update_dir, 1, repository); + +#ifdef SERVER_SUPPORT + if (server_active) + return err; +#endif + + /* see if we need to sleep before returning to avoid time-stamp races */ + if (last_register_time) + { + sleep_past (last_register_time); + } + + return err; +} + + + +#ifdef PRESERVE_PERMISSIONS_SUPPORT +/* + * The get_linkinfo_proc callback adds each file to the hardlist + * (see hardlink.c). + */ + +static int +get_linkinfo_proc( void *callerdat, struct file_info *finfo ) +{ + char *fullpath; + Node *linkp; + struct hardlink_info *hlinfo; + + /* Get the full pathname of the current file. */ + fullpath = xmalloc (strlen(working_dir) + + strlen(finfo->fullname) + 2); + sprintf (fullpath, "%s/%s", working_dir, finfo->fullname); + + /* To permit recursing into subdirectories, files + are keyed on the full pathname and not on the basename. */ + linkp = lookup_file_by_inode (fullpath); + if (linkp == NULL) + { + /* The file isn't on disk; we are probably restoring + a file that was removed. */ + return 0; + } + + /* Create a new, empty hardlink_info node. */ + hlinfo = (struct hardlink_info *) + xmalloc (sizeof (struct hardlink_info)); + + hlinfo->status = (Ctype) 0; /* is this dumb? */ + hlinfo->checked_out = 0; + + linkp->data = hlinfo; + + return 0; +} +#endif + + + +/* + * This is the callback proc for update. It is called for each file in each + * directory by the recursion code. The current directory is the local + * instantiation. file is the file name we are to operate on. update_dir is + * set to the path relative to where we started (for pretty printing). + * repository is the repository. entries and srcfiles are the pre-parsed + * entries and source control files. + * + * This routine decides what needs to be done for each file and does the + * appropriate magic for checkout + */ +static int +update_fileproc (void *callerdat, struct file_info *finfo) +{ + int retval, nb; + Ctype status; + Vers_TS *vers; + + 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. */ + if (rewrite_tag + && tag != NULL + && finfo->rcs != NULL) + { + char *rev = RCS_getversion (finfo->rcs, tag, NULL, 1, NULL); + if (rev != NULL + && nonbranch != (nb = !RCS_nodeisbranch (finfo->rcs, tag))) + { + if (nonbranch >= 0 && !warned && !quiet) + { + error (0, 0, +"warning: %s is a branch tag in some files and a revision tag in others.", + tag); + warned = 1; + } + if (nonbranch < nb) nonbranch = nb; + } + if (rev != NULL) + free (rev); + } + + if (pipeout) + { + /* + * We just return success without doing anything if any of the really + * funky cases occur + * + * If there is still a valid RCS file, do a regular checkout type + * operation + */ + switch (status) + { + case T_UNKNOWN: /* unknown file was explicitly asked + * about */ + case T_REMOVE_ENTRY: /* needs to be un-registered */ + case T_ADDED: /* added but not committed */ + retval = 0; + break; + case T_CONFLICT: /* old punt-type errors */ + retval = 1; + break; + case T_UPTODATE: /* file was already up-to-date */ + case T_NEEDS_MERGE: /* needs merging */ + case T_MODIFIED: /* locally modified */ + case T_REMOVED: /* removed but not committed */ + case T_CHECKOUT: /* needs checkout */ + case T_PATCH: /* needs patch */ + retval = checkout_file (finfo, vers, 0, 0, 0); + break; + + default: /* can't ever happen :-) */ + error (0, 0, + "unknown file status %d for file %s", status, finfo->file); + retval = 0; + break; + } + } + else + { + switch (status) + { + case T_UNKNOWN: /* unknown file was explicitly asked + * about */ + case T_UPTODATE: /* file was already up-to-date */ + retval = 0; + break; + case T_CONFLICT: /* old punt-type errors */ + retval = 1; + write_letter (finfo, 'C'); + break; + case T_NEEDS_MERGE: /* needs merging */ + if (! toss_local_changes) + { + retval = merge_file (finfo, vers); + break; + } + /* else FALL THROUGH */ + case T_MODIFIED: /* locally modified */ + retval = 0; + if (toss_local_changes) + { + char *bakname; + bakname = backup_file (finfo->file, vers->vn_user); + /* This behavior is sufficiently unexpected to + justify overinformativeness, I think. */ +#ifdef SERVER_SUPPORT + if ((! really_quiet) && (! server_active)) +#else /* ! SERVER_SUPPORT */ + if (! really_quiet) +#endif /* SERVER_SUPPORT */ + (void) printf ("(Locally modified %s moved to %s)\n", + finfo->file, bakname); + free (bakname); + + /* The locally modified file is still present, but + it will be overwritten by the repository copy + after this. */ + status = T_CHECKOUT; + retval = checkout_file (finfo, vers, 0, 0, 1); + } + else + { + if (vers->ts_conflict) + { + if (file_has_conflict (finfo, vers->ts_conflict) + || file_has_markers (finfo)) + { + write_letter (finfo, 'C'); + retval = 1; + } + else + { + /* Reregister to clear conflict flag. */ + Register (finfo->entries, finfo->file, + vers->vn_rcs, vers->ts_rcs, + vers->options, vers->tag, + vers->date, (char *)0); + } + } + if (!retval) + write_letter (finfo, 'M'); + } + break; + case T_PATCH: /* needs patch */ +#ifdef SERVER_SUPPORT + if (patches) + { + int docheckout; + struct stat file_info; + unsigned char checksum[16]; + + retval = patch_file (finfo, + vers, &docheckout, + &file_info, checksum); + if (! docheckout) + { + if (server_active && retval == 0) + server_updated (finfo, vers, + (rcs_diff_patches + ? SERVER_RCS_DIFF + : SERVER_PATCHED), + file_info.st_mode, checksum, + (struct buffer *) NULL); + break; + } + } +#endif + /* If we're not running as a server, just check the + file out. It's simpler and faster than producing + and applying patches. */ + /* Fall through. */ + case T_CHECKOUT: /* needs checkout */ + retval = checkout_file (finfo, vers, 0, 0, 1); + break; + case T_ADDED: /* added but not committed */ + write_letter (finfo, 'A'); + retval = 0; + break; + case T_REMOVED: /* removed but not committed */ + write_letter (finfo, 'R'); + retval = 0; + break; + case T_REMOVE_ENTRY: /* needs to be un-registered */ + retval = scratch_file (finfo, vers); + break; + default: /* can't ever happen :-) */ + error (0, 0, + "unknown file status %d for file %s", status, finfo->file); + retval = 0; + break; + } + } + + /* only try to join if things have gone well thus far */ + if (retval == 0 && join_rev1) + join_file (finfo, vers); + + /* if this directory has an ignore list, add this file to it */ + if (ignlist && (status != T_UNKNOWN || vers->ts_user == NULL)) + { + Node *p; + + p = getnode (); + p->type = FILES; + p->key = xstrdup (finfo->file); + if (addnode (ignlist, p) != 0) + freenode (p); + } + + freevers_ts (&vers); + return retval; +} + + + +static void +update_ignproc (const char *file, const char *dir) +{ + struct file_info finfo; + char *tmp; + + memset (&finfo, 0, sizeof (finfo)); + finfo.file = file; + finfo.update_dir = dir; + if (dir[0] == '\0') + tmp = xstrdup (file); + else + { + tmp = xmalloc (strlen (file) + strlen (dir) + 10); + strcpy (tmp, dir); + strcat (tmp, "/"); + strcat (tmp, file); + } + + finfo.fullname = tmp; + write_letter (&finfo, '?'); + free (tmp); +} + + + +/* ARGSUSED */ +static int +update_filesdone_proc (void *callerdat, int err, const char *repository, + const char *update_dir, List *entries) +{ + if (nonbranch < 0) nonbranch = 0; + 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, entries, update_dir, update_ignproc); + dellist (&ignlist); + } + + /* Clean up CVS admin dirs if we are export */ + if (strcmp (cvs_cmd_name, "export") == 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 (CVSADM) < 0 && !existence_error (errno)) + error (0, errno, "cannot remove %s directory", CVSADM); + } +#ifdef SERVER_SUPPORT + else if (!server_active && !pipeout) +#else + else if (!pipeout) +#endif /* SERVER_SUPPORT */ + { + /* If there is no CVS/Root file, add one */ + if (!isfile (CVSADM_ROOT)) + Create_Root (NULL, current_parsed_root->original); + } + + return err; +} + + + +/* + * update_dirent_proc () is called back by the recursion processor before a + * sub-directory is processed for update. In this case, update_dirent proc + * will probably create the directory unless -d isn't specified and this is a + * new directory. A return code of 0 indicates the directory should be + * processed by the recursion code. A return of non-zero indicates the + * recursion code should skip this directory. + */ +static Dtype +update_dirent_proc (void *callerdat, const char *dir, const char *repository, + const 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)) + { + /* if we aren't building dirs, blow it off */ + if (!update_build_dirs) + return R_SKIP_ALL; + + /* Various CVS administrators are in the habit of removing + the repository directory for things they don't want any + more. I've even been known to do it myself (on rare + occasions). Not the usual recommended practice, but we + want to try to come up with some kind of + reasonable/documented/sensible behavior. Generally + the behavior is to just skip over that directory (see + dirs test in sanity.sh; the case which reaches here + is when update -d is specified, and the working directory + is gone but the subdirectory is still mentioned in + CVS/Entries). */ + if (1 +#ifdef SERVER_SUPPORT + /* In the remote case, the client should refrain from + sending us the directory in the first place. So we + want to continue to give an error, so clients make + sure to do this. */ + && !server_active +#endif + && !isdir (repository)) + return R_SKIP_ALL; + + if (noexec) + { + /* ignore the missing dir if -n is specified */ + error (0, 0, "New directory `%s' -- ignored", update_dir); + return R_SKIP_ALL; + } + else + { + /* otherwise, create the dir and appropriate adm files */ + + /* If no tag or date were specified on the command line, + and we're not using -A, we want the subdirectory to use + the tag and date, if any, of the current directory. + That way, update -d will work correctly when working on + a branch. + + We use TAG_UPDATE_DIR to undo the tag setting in + update_dirleave_proc. If we did not do this, we would + not correctly handle a working directory with multiple + tags (and maybe we should prohibit such working + directories, but they work now and we shouldn't make + them stop working without more thought). */ + if ((tag == NULL && date == NULL) && ! aflag) + { + ParseTag (&tag, &date, &nonbranch); + if (tag != NULL || date != NULL) + tag_update_dir = xstrdup (update_dir); + } + + make_directory (dir); + Create_Admin (dir, update_dir, repository, tag, date, + /* This is a guess. We will rewrite it later + via WriteTag. */ + 0, + 0, + dotemplate); + rewrite_tag = 1; + nonbranch = -1; + warned = 0; + Subdir_Register (entries, NULL, dir); + } + } + /* Do we need to check noexec here? */ + else if (!pipeout) + { + char *cvsadmdir; + + /* The directory exists. Check to see if it has a CVS + subdirectory. */ + + cvsadmdir = xmalloc (strlen (dir) + 80); + strcpy (cvsadmdir, dir); + strcat (cvsadmdir, "/"); + strcat (cvsadmdir, CVSADM); + + if (!isdir (cvsadmdir)) + { + /* We cannot successfully recurse into a directory without a CVS + subdirectory. Generally we will have already printed + "? foo". */ + free (cvsadmdir); + return R_SKIP_ALL; + } + free (cvsadmdir); + } + + /* + * If we are building dirs and not going to stdout, we make sure there is + * no static entries file and write the tag file as appropriate + */ + if (!pipeout) + { + if (update_build_dirs) + { + 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); +#ifdef SERVER_SUPPORT + 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, 0, update_dir, repository); + rewrite_tag = 1; + nonbranch = -1; + warned = 0; + } + + WriteTemplate (update_dir, dotemplate, repository); + + /* initialize the ignore list for this directory */ + ignlist = getlist (); + } + + /* print the warm fuzzy message */ + if (!quiet) + error (0, 0, "Updating %s", update_dir); + + return R_PROCESS; +} + + + +/* + * update_dirleave_proc () is called back by the recursion code upon leaving + * a directory. It will prune empty directories if needed and will execute + * any appropriate update programs. + */ +/* ARGSUSED */ +static int +update_dirleave_proc (void *callerdat, const char *dir, int err, + const char *update_dir, List *entries) +{ + /* Delete the ignore list if it hasn't already been done. */ + if (ignlist) + dellist (&ignlist); + + /* If we set the tag or date for a new subdirectory in + update_dirent_proc, and we're now done with that subdirectory, + undo the tag/date setting. Note that we know that the tag and + date were both originally NULL in this case. */ + if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0) + { + if (tag != NULL) + { + free (tag); + tag = NULL; + } + if (date != NULL) + { + free (date); + date = NULL; + } + nonbranch = -1; + warned = 0; + free (tag_update_dir); + tag_update_dir = NULL; + } + + if (strchr (dir, '/') == NULL) + { + /* 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, NULL, dir); + } + } + + return err; +} + + + +/* Returns 1 if the file indicated by node has been removed. */ +static int +isremoved (Node *node, void *closure) +{ + Entnode *entdata = 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 (const char *dir, int might_not_exist) +{ + DIR *dirp; + struct dirent *dp; + + if ((dirp = CVS_OPENDIR (dir)) == NULL) + { + 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 = CVS_READDIR (dirp)) != NULL) + { + if (strcmp (dp->d_name, ".") != 0 + && strcmp (dp->d_name, "..") != 0) + { + if (strcmp (dp->d_name, CVSADM) != 0) + { + /* An entry other than the CVS directory. The directory + is certainly not empty. */ + (void) CVS_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)) + exit (EXIT_FAILURE); + + if (CVS_CHDIR (dir) < 0) + error (1, errno, "cannot change directory to %s", dir); + l = Entries_Open (0, NULL); + files_removed = walklist (l, isremoved, 0); + Entries_Close (l); + + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + + if (files_removed != 0) + { + /* There are files that have been removed, but not + committed! Do not consider the directory empty. */ + (void) CVS_CLOSEDIR (dirp); + return 0; + } + } + } + errno = 0; + } + if (errno != 0) + { + error (0, errno, "cannot read directory %s", dir); + (void) CVS_CLOSEDIR (dirp); + return 0; + } + (void) CVS_CLOSEDIR (dirp); + return 1; +} + + + +/* + * scratch the Entries file entry associated with a file + */ +static int +scratch_file (struct file_info *finfo, Vers_TS *vers) +{ + history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository); + Scratch_Entry (finfo->entries, finfo->file); +#ifdef SERVER_SUPPORT + if (server_active) + { + if (vers->ts_user == NULL) + server_scratch_entry_only (); + server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1, NULL, NULL); + } +#endif + if (unlink_file (finfo->file) < 0 && ! existence_error (errno)) + error (0, errno, "unable to remove %s", finfo->fullname); + else +#ifdef SERVER_SUPPORT + /* skip this step when the server is running since + * server_updated should have handled it */ + if (!server_active) +#endif + { + /* keep the vers structure up to date in case we do a join + * - if there isn't a file, it can't very well have a version number, can it? + */ + if (vers->vn_user != NULL) + { + free (vers->vn_user); + vers->vn_user = NULL; + } + if (vers->ts_user != NULL) + { + free (vers->ts_user); + vers->ts_user = NULL; + } + } + return 0; +} + + + +/* + * Check out a file. + */ +static int +checkout_file (struct file_info *finfo, Vers_TS *vers_ts, int adding, + int merging, int update_server) +{ + char *backup; + int set_time, retval = 0; + int status; + int file_is_dead; + struct buffer *revbuf; + + backup = NULL; + revbuf = NULL; + + /* Don't screw with backup files if we're going to stdout, or if + we are the server. */ + if (!pipeout +#ifdef SERVER_SUPPORT + && ! server_active +#endif + ) + { + 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 + { + /* If -f/-t wrappers are being used to wrap up a directory, + then backup might be a directory instead of just a file. */ + if (unlink_file_dir (backup) < 0) + { + /* Not sure if the existence_error check is needed here. */ + if (!existence_error (errno)) + /* FIXME: should include update_dir in message. */ + error (0, errno, "error removing %s", backup); + } + free (backup); + backup = NULL; + } + } + + file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs); + + if (!file_is_dead) + { + /* + * if we are checking out to stdout, print a nice message to + * stderr, and add the -p flag to the command */ + if (pipeout) + { + if (!quiet) + { + 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); + } + } + +#ifdef SERVER_SUPPORT + if (update_server + && server_active + && ! pipeout + && ! file_gzip_level + && ! joining () + && ! wrap_name_has (finfo->file, WRAP_FROMCVS)) + { + revbuf = buf_nonio_initialize (NULL); + status = RCS_checkout (vers_ts->srcfile, NULL, + vers_ts->vn_rcs, vers_ts->tag, + vers_ts->options, RUN_TTY, + checkout_to_buffer, revbuf); + } + else +#endif + status = RCS_checkout (vers_ts->srcfile, + pipeout ? NULL : finfo->file, + vers_ts->vn_rcs, vers_ts->tag, + vers_ts->options, RUN_TTY, NULL, NULL); + } + if (file_is_dead || status == 0) + { + mode_t mode; + + mode = (mode_t) -1; + + if (!pipeout) + { + Vers_TS *xvers_ts; + + if (revbuf != NULL && !noexec) + { + struct stat sb; + + /* FIXME: We should have RCS_checkout return the mode. + That would also fix the kludge with noexec, above, which + is here only because noexec doesn't write srcfile->path + for us to stat. */ + if( CVS_STAT( vers_ts->srcfile->path, &sb ) < 0 ) + { + buf_free (revbuf); + error (1, errno, "cannot stat %s", + vers_ts->srcfile->path); + } + mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH); + } + + if (cvswrite + && !file_is_dead + && !fileattr_get (finfo->file, "_watched")) + { + if (revbuf == NULL) + xchmod (finfo->file, 1); + else + { + /* We know that we are the server here, so + although xchmod checks umask, we don't bother. */ + mode |= (((mode & S_IRUSR) ? S_IWUSR : 0) + | ((mode & S_IRGRP) ? S_IWGRP : 0) + | ((mode & S_IROTH) ? S_IWOTH : 0)); + } + } + + { + /* A newly checked out file is never under the spell + of "cvs edit". If we think we were editing it + from a previous life, clean up. Would be better to + check for same the working directory instead of + same user, but that is hairy. */ + + struct addremove_args args; + + editor_set (finfo->file, getcaller (), NULL); + + memset (&args, 0, sizeof args); + args.remove_temp = 1; + watch_modify_watchers (finfo->file, &args); + } + + /* set the time from the RCS file iff it was unknown before */ + set_time = + (!noexec + && (vers_ts->vn_user == NULL || + strncmp (vers_ts->ts_rcs, "Initial", 7) == 0) + && !file_is_dead); + + wrap_fromcvs_process_file (finfo->file); + + 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'; + + if (revbuf != NULL) + { + /* If we stored the file data into a buffer, then we + didn't create a file at all, so xvers_ts->ts_user + is wrong. The correct value is to have it be the + same as xvers_ts->ts_rcs, meaning that the working + file is unchanged from the RCS file. + + FIXME: We should tell Version_TS not to waste time + statting the nonexistent file. + + FIXME: Actually, I don't think the ts_user value + matters at all here. The only use I know of is + that it is printed in a trace message by + Server_Register. */ + + if (xvers_ts->ts_user != NULL) + free (xvers_ts->ts_user); + xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs); + } + + (void) time (&last_register_time); + + if (file_is_dead) + { + if (xvers_ts->vn_user != NULL) + { + error (0, 0, + "warning: %s is not (any longer) pertinent", + finfo->fullname); + } + 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)) + { + error (0, errno, "cannot remove %s", finfo->fullname); + } + } + else + Register (finfo->entries, finfo->file, + adding ? "0" : xvers_ts->vn_rcs, + xvers_ts->ts_user, xvers_ts->options, + xvers_ts->tag, xvers_ts->date, + NULL); /* Clear conflict flag on fresh checkout */ + + /* fix up the vers structure, in case it is used by join */ + if (join_rev1) + { + /* FIXME: Throwing away the original revision info is almost + certainly wrong -- what if join_rev1 is "BASE"? */ + if (vers_ts->vn_user != NULL) + free (vers_ts->vn_user); + if (vers_ts->vn_rcs != NULL) + free (vers_ts->vn_rcs); + vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs); + vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs); + } + + /* If this is really Update and not Checkout, recode history */ + if (strcmp (cvs_cmd_name, "update") == 0) + 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 (finfo, 'U'); + } + } + +#ifdef SERVER_SUPPORT + if (update_server && server_active) + server_updated (finfo, vers_ts, + merging ? SERVER_MERGED : SERVER_UPDATED, + mode, (unsigned char *) NULL, revbuf); +#endif + } + else + { + if (backup != NULL) + { + rename_file (backup, finfo->file); + free (backup); + backup = NULL; + } + + error (0, 0, "could not check out %s", finfo->fullname); + + retval = status; + } + + if (backup != NULL) + { + /* If -f/-t wrappers are being used to wrap up a directory, + then backup might be a directory instead of just a file. */ + if (unlink_file_dir (backup) < 0) + { + /* Not sure if the existence_error check is needed here. */ + if (!existence_error (errno)) + /* FIXME: should include update_dir in message. */ + error (0, errno, "error removing %s", backup); + } + free (backup); + } + + if (revbuf != NULL) + buf_free (revbuf); + return retval; +} + + + +#ifdef SERVER_SUPPORT + +/* This function is used to write data from a file being checked out + into a buffer. */ + +static void +checkout_to_buffer (void *callerdat, const char *data, size_t len) +{ + struct buffer *buf = (struct buffer *) callerdat; + + buf_output (buf, data, len); +} + +#endif /* SERVER_SUPPORT */ + +#ifdef SERVER_SUPPORT + +/* 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 cvs_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 (struct file_info *finfo, Vers_TS *vers_ts, int *docheckout, struct stat *file_info, unsigned char *checksum) +{ + char *backup; + char *file1; + char *file2; + int retval = 0; + int retcode = 0; + int fail; + FILE *e; + struct patch_file_data data; + + *docheckout = 0; + + if (noexec || pipeout || joining ()) + { + *docheckout = 1; + return 0; + } + + /* 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; + } + + /* First check that the first revision exists. If it has been nuked + by cvs admin -o, then just fall back to checking out entire + revisions. In some sense maybe we don't have to do this; after + all cvs.texinfo says "Make sure that no-one has checked out a + copy of the revision you outdate" but then again, that advice + doesn't really make complete sense, because "cvs admin" operates + on a working directory and so _someone_ will almost always have + _some_ revision checked out. */ + { + char *rev; + + rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL); + if (rev == NULL) + { + *docheckout = 1; + return 0; + } + else + free (rev); + } + + /* If the revision is dead, let checkout_file handle it rather + than duplicating the processing here. */ + if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs)) + { + *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 + { + if (unlink_file (backup) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", backup); + } + + 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. */ + + 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; + + /* FIXME - Passing vers_ts->tag here is wrong in the least number + * of cases. Since we don't know whether vn_user was checked out + * using a tag, we pass vers_ts->tag, which, assuming the user did + * not specify a new TAG to -r, will be the branch we are on. + * + * The only thing it is used for is to substitute in for the Name + * RCS keyword, so in the error case, the patch fails to apply on + * the client end and we end up resending the whole file. + * + * At least, if we are keeping track of the tag vn_user came from, + * I don't know where yet. -DRP + */ + retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL, + vers_ts->vn_user, vers_ts->tag, + 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) + { + e = CVS_FOPEN (file2, "w"); + if (e == NULL) + error (1, errno, "cannot open %s", file2); + + data.filename = file2; + data.fp = e; + data.final_nl = 0; + data.compute_checksum = 1; + cvs_MD5Init (&data.context); + + retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL, + vers_ts->vn_rcs, vers_ts->tag, + vers_ts->options, RUN_TTY, + patch_file_write, (void *) &data); + + if (fclose (e) < 0) + error (1, errno, "cannot close %s", file2); + + if (retcode != 0 || ! data.final_nl) + fail = 1; + else + cvs_MD5Final (checksum, &data.context); + } + + retcode = 0; + if (! fail) + { + char *diff_options; + + /* 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 that is what CVS has + traditionally used. Kind of a moot point, now that + Rcs-diff is preferred, so there is no point in making + the compatibility issues worse. */ + diff_options = "-c"; + } + else + { + /* Now that diff is librarified, we could be passing -a if + we wanted to. However, it is unclear to me whether we + would want to. Does diff -a, in any significant + percentage of cases, produce patches which are smaller + than the files it is patching? I guess maybe text + files with character sets which diff regards as + 'binary'. Conversely, do they tend to be much larger + in the bad cases? This needs some more + thought/investigation, I suspect. */ + + diff_options = "-n"; + } + retcode = diff_exec (file1, file2, NULL, NULL, diff_options, finfo->file); + + /* A retcode of 0 means no differences. 1 means some differences. */ + if (retcode != 0 + && retcode != 1) + { + fail = 1; + } + } + + if (! fail) + { + struct stat file2_info; + + /* Check to make sure the patch is really shorter */ + if (CVS_STAT (file2, &file2_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 (file2_info.st_size <= file_info->st_size) + fail = 1; + } + + if (! fail) + { +# define BINARY "Binary" + char buf[sizeof BINARY]; + unsigned int c; + + /* Check the diff output to make sure patch will be handle it. */ + e = CVS_FOPEN (finfo->file, "r"); + if (e == NULL) + 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) + { + /* These are binary files. We could use diff -a, but + patch can't handle that. */ + fail = 1; + } + fclose (e); + } + + if (! fail) + { + Vers_TS *xvers_ts; + + /* 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 + && !fileattr_get (finfo->file, "_watched")) + xchmod (finfo->file, 1); + + /* This stuff is just copied blindly from checkout_file. I + don't really know what it does. */ + 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 (finfo->entries, finfo->file, xvers_ts->vn_rcs, + xvers_ts->ts_user, xvers_ts->options, + xvers_ts->tag, xvers_ts->date, NULL); + + 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, record history. */ + if (strcmp (cvs_cmd_name, "update") == 0) + history_write ('P', finfo->update_dir, xvers_ts->vn_rcs, + finfo->file, finfo->repository); + + freevers_ts (&xvers_ts); + + if (!really_quiet) + { + write_letter (finfo, 'P'); + } + } + else + { + int old_errno = errno; /* save errno value over the rename */ + + if (isfile (backup)) + rename_file (backup, finfo->file); + + if (retcode != 0 && retcode != 1) + error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0, + "could not diff %s", finfo->fullname); + + *docheckout = 1; + retval = retcode; + } + + if (unlink_file (backup) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", backup); + if (unlink_file (file1) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", file1); + if (unlink_file (file2) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", file2); + + free (backup); + free (file1); + free (file2); + return retval; +} + + + +/* 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 (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) + cvs_MD5Update (&data->context, (unsigned char *) buffer, len); +} + +#endif /* SERVER_SUPPORT */ + +/* + * Several of the types we process only print a bit of information consisting + * of a single letter and the name. + */ +void +write_letter (struct file_info *finfo, int letter) +{ + if (!really_quiet) + { + char *tag = NULL; + /* Big enough for "+updated" or any of its ilk. */ + char buf[80]; + + switch (letter) + { + case 'U': + tag = "updated"; + break; + default: + /* We don't yet support tagged output except for "U". */ + break; + } + + if (tag != NULL) + { + sprintf (buf, "+%s", tag); + cvs_output_tagged (buf, NULL); + } + buf[0] = letter; + buf[1] = ' '; + buf[2] = '\0'; + cvs_output_tagged ("text", buf); + cvs_output_tagged ("fname", finfo->fullname); + cvs_output_tagged ("newline", NULL); + if (tag != NULL) + { + sprintf (buf, "-%s", tag); + cvs_output_tagged (buf, NULL); + } + } + return; +} + + + +/* + * Do all the magic associated with a file which needs to be merged + */ +static int +merge_file (struct file_info *finfo, Vers_TS *vers) +{ + char *backup; + int status; + int retval; + + /* + * The users currently modified file is moved to a backup file name + * ".#filename.version", so that it will stay around for a few days + * before being automatically removed by some cron daemon. The "version" + * is the version of the file that the user was most up-to-date with + * before the merge. + */ + backup = xmalloc (strlen (finfo->file) + + strlen (vers->vn_user) + + sizeof (BAKPREFIX) + + 10); + (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user); + + if (unlink_file (backup) && !existence_error (errno)) + error (0, errno, "unable to remove %s", backup); + copy_file (finfo->file, backup); + xchmod (finfo->file, 1); + + if (strcmp (vers->options, "-kb") == 0 + || wrap_merge_is_copy (finfo->file) + || special_file_mismatch (finfo, NULL, vers->vn_rcs)) + { + /* For binary files, a merge is always a conflict. Same for + files whose permissions or linkage do not match. 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. */ + + /* TODO: it may not always be necessary to regard a permission + mismatch as a conflict. The working file and the RCS file + have a common ancestor `A'; if the working file's permissions + match A's, then it's probably safe to overwrite them with the + RCS permissions. Only if the working file, the RCS file, and + A all disagree should this be considered a conflict. But more + thought needs to go into this, and in the meantime it is safe + to treat any such mismatch as an automatic conflict. -twp */ + +#ifdef SERVER_SUPPORT + if (server_active) + server_copy_file (finfo->file, finfo->update_dir, + finfo->repository, backup); +#endif + + status = checkout_file (finfo, vers, 0, 1, 1); + + /* Is there a better term than "nonmergeable file"? What we + really mean is, not something that CVS cannot or does not + want to merge (there might be an external manual or + automatic merge process). */ + error (0, 0, "nonmergeable 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, 'C'); + + history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, + finfo->repository); + retval = 0; + goto out; + } + + status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file, + 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, finfo->fullname); + error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s", + finfo->fullname, backup); + rename_file (backup, finfo->file); + retval = 1; + goto out; + } + + if (strcmp (vers->options, "-V4") == 0) + vers->options[0] = '\0'; + + /* This file is the result of a merge, which means that it has + been modified. We use a special timestamp string which will + not compare equal to any actual timestamp. */ + { + char *cp = 0; + + if (status) + { + (void) time (&last_register_time); + cp = time_stamp (finfo->file); + } + Register (finfo->entries, finfo->file, vers->vn_rcs, + "Result of merge", vers->options, vers->tag, + vers->date, cp); + if (cp) + free (cp); + } + + /* fix up the vers structure, in case it is used by join */ + if (join_rev1) + { + /* FIXME: Throwing away the original revision info is almost + certainly wrong -- what if join_rev1 is "BASE"? */ + if (vers->vn_user != NULL) + free (vers->vn_user); + vers->vn_user = xstrdup (vers->vn_rcs); + } + +#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, + (mode_t) -1, (unsigned char *) NULL, + (struct buffer *) NULL); + } +#endif + + if (status == 1) + { + error (0, 0, "conflicts found in %s", finfo->fullname); + + write_letter (finfo, 'C'); + + history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, + finfo->repository); + + } + else /* status == 0 */ + { + history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file, + finfo->repository); + + /* FIXME: the noexec case is broken. RCS_merge could be doing the + xcmp on the temporary files without much hassle, I think. */ + if (!noexec && !xcmp (backup, finfo->file)) + { + cvs_output (finfo->fullname, 0); + cvs_output (" already contains the differences between ", 0); + cvs_output (vers->vn_user, 0); + cvs_output (" and ", 0); + cvs_output (vers->vn_rcs, 0); + cvs_output ("\n", 1); + + retval = 0; + goto out; + } + + write_letter (finfo, 'M'); + } + retval = 0; + out: + free (backup); + return retval; +} + + + +/* + * Do all the magic associated with a file which needs to be joined + * (reached via the -j option to checkout or update). + * + * INPUTS + * finfo File information about the destination file. + * vers The Vers_TS structure for finfo. + * + * GLOBALS + * join_rev1 From the command line. + * join_rev2 From the command line. + * server_active Natch. + * + * ASSUMPTIONS + * 1. Is not called in client mode. + */ +static void +join_file (struct file_info *finfo, Vers_TS *vers) +{ + char *backup; + char *t_options; + int status; + + char *rev1; + char *rev2; + char *jrev1; + char *jrev2; + char *jdate1; + char *jdate2; + + TRACE ( 1, "join_file(%s, %s%s%s%s, %s, %s)", + finfo->file, + vers->tag ? vers->tag : "", + vers->tag ? " (" : "", + vers->vn_rcs ? vers->vn_rcs : "", + vers->tag ? ")" : "", + join_rev1 ? join_rev1 : "", + join_rev2 ? join_rev2 : "" ); + + jrev1 = join_rev1; + jrev2 = join_rev2; + jdate1 = date_rev1; + jdate2 = date_rev2; + + /* Determine if we need to do anything at all. */ + if (vers->srcfile == NULL || + vers->srcfile->path == NULL) + { + return; + } + + /* If only one join revision is specified, it becomes the second + revision. */ + if (jrev2 == NULL) + { + jrev2 = jrev1; + jrev1 = NULL; + jdate2 = jdate1; + jdate1 = NULL; + } + + /* FIXME: Need to handle "BASE" for jrev1 and/or jrev2. Note caveat + below about vn_user. */ + + /* 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 + { + /* 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 + rev1 = gca (vers->vn_rcs, rev2); + } + + /* Handle a nonexistent or dead merge target. */ + if (rev2 == NULL || RCS_isdead (vers->srcfile, rev2)) + { + char *mrev; + + if (rev2 != NULL) + free (rev2); + + /* 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; + } + + /* 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, + "file %s is locally modified, but has been removed in revision %s as of %s", + finfo->fullname, jrev2, jdate2); + else + error (0, 0, + "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. + + The same goes for a removal then an add. e.g. + cvs up -rbr -jbr2 could remove and readd the same file + */ + /* save the rev since server_updated might invalidate it */ + mrev = xmalloc (strlen (vers->vn_user) + 2); + sprintf (mrev, "-%s", vers->vn_user); +#ifdef SERVER_SUPPORT + if (server_active) + { + server_scratch (finfo->file); + server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1, + (unsigned char *) NULL, (struct buffer *) NULL); + } +#endif + 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; + } + + /* If the two merge revisions are the same, then there is nothing + * to do. This needs to be checked before the rev2 == up-to-date base + * revision check tha comes next. Otherwise, rev1 can == rev2 and get an + * "already contains the changes between and " message. + */ + if (rev1 && strcmp (rev1, rev2) == 0) + { + free (rev1); + free (rev2); + return; + } + + /* If we know that the user file is up-to-date, then it becomes an + * optimization to skip the merge when rev2 is the same as the base + * revision. i.e. we know that diff3(file2,file1,file2) will produce + * file2. + */ + if (vers->vn_user != NULL && vers->ts_user != NULL + && strcmp (vers->ts_user, vers->ts_rcs) == 0 + && strcmp (rev2, vers->vn_user) == 0) + { + if (!really_quiet) + { + cvs_output (finfo->fullname, 0); + cvs_output (" already contains the differences between ", 0); + cvs_output (rev1 ? rev1 : "creation", 0); + cvs_output (" and ", 0); + cvs_output (rev2, 0); + cvs_output ("\n", 1); + } + + if (rev1 != NULL) + free (rev1); + free (rev2); + + return; + } + + /* 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)) + { + if (rev1 != NULL) + free (rev1); + free (rev2); + + /* 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) + { + char *saved_options = options; + Vers_TS *xvers; + + xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0); + + /* Reset any keyword expansion option. Otherwise, when a + command like `cvs update -kk -jT1 -jT2' creates a new file + (because a file had the T2 tag, but not T1), the subsequent + commit of that just-added file effectively would set the + admin `-kk' option for that file in the repository. */ + options = NULL; + + /* FIXME: If checkout_file fails, we should arrange to + return a non-zero exit status. */ + status = checkout_file (finfo, xvers, 1, 0, 1); + options = saved_options; + + freevers_ts (&xvers); + + 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; + } + + /* If there is no working file, then we can't do the merge. */ + if (vers->vn_user == NULL || vers->vn_user[0] == '-') + { + free (rev1); + free (rev2); + + if (jdate2 != NULL) + error (0, 0, + "file %s does not exist, but is present in revision %s as of %s", + finfo->fullname, jrev2, jdate2); + else + error (0, 0, + "file %s does not exist, but is present in revision %s", + finfo->fullname, jrev2); + + /* FIXME: Should we arrange to return a non-zero exit status? */ + + return; + } + +#ifdef SERVER_SUPPORT + if (server_active && !isreadable (finfo->file)) + { + int retcode; + /* The file is up to date. Need to check out the current contents. */ + /* FIXME - see the FIXME comment above the call to RCS_checkout in the + * patch_file function. + */ + retcode = RCS_checkout (vers->srcfile, finfo->file, + vers->vn_user, vers->tag, + (char *) NULL, RUN_TTY, + (RCSCHECKOUTPROC) NULL, (void *) NULL); + if (retcode != 0) + error (1, 0, + "failed to check out %s file", finfo->fullname); + } +#endif + + /* + * The users currently modified file is moved to a backup file name + * ".#filename.version", so that it will stay around for a few days + * before being automatically removed by some cron daemon. The "version" + * is the version of the file that the user was most up-to-date with + * before the merge. + */ + backup = xmalloc (strlen (finfo->file) + + strlen (vers->vn_user) + + sizeof (BAKPREFIX) + + 10); + (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user); + + if (unlink_file (backup) < 0 + && !existence_error (errno)) + error (0, errno, "cannot remove %s", backup); + copy_file (finfo->file, backup); + xchmod (finfo->file, 1); + + t_options = vers->options; +#if 0 + if (*t_options == '\0') + t_options = "-kk"; /* to ignore keyword expansions */ +#endif + + /* If the source of the merge is the same as the working file + revision, then we can just RCS_checkout the target (no merging + as such). In the text file case, this is probably quite + similar to the RCS_merge, but in the binary file case, + RCS_merge gives all kinds of trouble. */ + if (vers->vn_user != NULL + && strcmp (rev1, vers->vn_user) == 0 + /* See comments above about how No_Difference has already been + called. */ + && vers->ts_user != NULL + && strcmp (vers->ts_user, vers->ts_rcs) == 0 + + /* Avoid this in the text file case. See below for why. + */ + && (strcmp (t_options, "-kb") == 0 + || wrap_merge_is_copy (finfo->file))) + { + /* FIXME: Verify my comment below: + * + * RCS_merge does nothing with keywords. It merges the changes between + * two revisions without expanding the keywords (it might expand in + * -kk mode before computing the diff between rev1 and rev2 - I'm not + * sure). In other words, the keyword lines in the current work file + * get left alone. + * + * Therfore, checking out the destination revision (rev2) is probably + * incorrect in the text case since we should see the keywords that were + * substituted into the original file at the time it was checked out + * and not the keywords from rev2. + * + * Also, it is safe to pass in NULL for nametag since we know no + * substitution is happening during the binary mode checkout. + */ + if (RCS_checkout ( finfo->rcs, finfo->file, rev2, (char *)NULL, t_options, + RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0 ) + status = 2; + else + status = 0; + + /* OK, this is really stupid. RCS_checkout carefully removes + write permissions, and we carefully put them back. But + until someone gets around to fixing it, that seems like the + easiest way to get what would seem to be the right mode. + I don't check CVSWRITE or _watched; I haven't thought about + that in great detail, but it seems like a watched file should + be checked out (writable) after a merge. */ + xchmod (finfo->file, 1); + + /* Traditionally, the text file case prints a whole bunch of + scary looking and verbose output which fails to tell the user + what is really going on (it gives them rev1 and rev2 but doesn't + indicate in any way that rev1 == vn_user). I think just a + simple "U foo" is good here; it seems analogous to the case in + which the file was added on the branch in terms of what to + print. */ + write_letter (finfo, 'U'); + } + else if (strcmp (t_options, "-kb") == 0 + || wrap_merge_is_copy (finfo->file) + || special_file_mismatch (finfo, rev1, rev2)) + { + /* We are dealing with binary files, or files with a + permission/linkage mismatch (this second case only occurs when + PRESERVE_PERMISSIONS_SUPPORT is enabled), and real merging would + need to take place. This is 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. */ + if (RCS_checkout ( finfo->rcs, finfo->file, rev2, (char *)NULL, + t_options, RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0) + status = 2; + else + status = 0; + + /* OK, this is really stupid. RCS_checkout carefully removes + write permissions, and we carefully put them back. But + until someone gets around to fixing it, that seems like the + easiest way to get what would seem to be the right mode. + I don't check CVSWRITE or _watched; I haven't thought about + that in great detail, but it seems like a watched file should + be checked out (writable) after a merge. */ + xchmod (finfo->file, 1); + + /* Hmm. We don't give them REV1 anywhere. I guess most people + probably don't have a 3-way merge tool for the file type in + question, and might just get confused if we tried to either + provide them with a copy of the file from REV1, or even just + told them what REV1 is so they can get it themself, but it + might be worth thinking about. */ + /* See comment in merge_file about the "nonmergeable file" + terminology. */ + error (0, 0, "nonmergeable file needs merge"); + error (0, 0, "revision %s from repository is now in %s", + rev2, finfo->fullname); + error (0, 0, "file from working directory is now in %s", backup); + write_letter (finfo, 'C'); + } + else + status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file, + t_options, rev1, rev2); + + if (status != 0) + { + if (status != 1) + { + error (0, status == -1 ? errno : 0, + "could not merge revision %s of %s", rev2, finfo->fullname); + error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s", + finfo->fullname, backup); + rename_file (backup, finfo->file); + } + } + else /* status == 0 */ + { + /* FIXME: the noexec case is broken. RCS_merge could be doing the + xcmp on the temporary files without much hassle, I think. */ + if (!noexec && !xcmp (backup, finfo->file)) + { + if (!really_quiet) + { + cvs_output (finfo->fullname, 0); + cvs_output (" already contains the differences between ", 0); + cvs_output (rev1, 0); + cvs_output (" and ", 0); + cvs_output (rev2, 0); + cvs_output ("\n", 1); + } + + /* and skip the registering and sending the new file since it + * hasn't been updated. + */ + goto out; + } + } + + /* The file has changed, but if we just checked it out it may + still have the same timestamp it did when it was first + registered above in checkout_file. We register it again with a + dummy timestamp to make sure that later runs of CVS will + recognize that it has changed. + + We don't actually need to register again if we called + RCS_checkout above, and we aren't running as the server. + However, that is not the normal case, and calling Register + again won't cost much in that case. */ + { + char *cp = 0; + + if (status) + { + (void) time (&last_register_time); + cp = time_stamp (finfo->file); + } + Register (finfo->entries, finfo->file, + vers->vn_rcs ? vers->vn_rcs : "0", "Result of merge", + vers->options, vers->tag, vers->date, cp); + if (cp) + free(cp); + } + +#ifdef SERVER_SUPPORT + if (server_active) + { + server_copy_file (finfo->file, finfo->update_dir, finfo->repository, + backup); + server_updated (finfo, vers, SERVER_MERGED, + (mode_t) -1, (unsigned char *) NULL, + (struct buffer *) NULL); + } +#endif + +out: + free (rev1); + free (rev2); + free (backup); +} + + + +/* + * Report whether revisions REV1 and REV2 of FINFO agree on: + * . file ownership + * . permissions + * . major and minor device numbers + * . symbolic links + * . hard links + * + * If either REV1 or REV2 is NULL, the working copy is used instead. + * + * Return 1 if the files differ on these data. + */ + +int +special_file_mismatch (struct file_info *finfo, char *rev1, char *rev2) +{ +#ifdef PRESERVE_PERMISSIONS_SUPPORT + struct stat sb; + RCSVers *vp; + Node *n; + uid_t rev1_uid, rev2_uid; + gid_t rev1_gid, rev2_gid; + mode_t rev1_mode, rev2_mode; + unsigned long dev_long; + dev_t rev1_dev, rev2_dev; + char *rev1_symlink = NULL; + char *rev2_symlink = NULL; + List *rev1_hardlinks = NULL; + List *rev2_hardlinks = NULL; + int check_uids, check_gids, check_modes; + int result; + + /* If we don't care about special file info, then + don't report a mismatch in any case. */ + if (!preserve_perms) + return 0; + + /* When special_file_mismatch is called from No_Difference, the + RCS file has been only partially parsed. We must read the + delta tree in order to compare special file info recorded in + the delta nodes. (I think this is safe. -twp) */ + if (finfo->rcs->flags & PARTIAL) + RCS_reparsercsfile (finfo->rcs, NULL, NULL); + + check_uids = check_gids = check_modes = 1; + + /* Obtain file information for REV1. If this is null, then stat + finfo->file and use that info. */ + /* If a revision does not know anything about its status, + then presumably it doesn't matter, and indicates no conflict. */ + + if (rev1 == NULL) + { + if (islink (finfo->file)) + rev1_symlink = xreadlink (finfo->file); + else + { +# ifdef HAVE_STRUCT_STAT_ST_RDEV + if (CVS_LSTAT (finfo->file, &sb) < 0) + error (1, errno, "could not get file information for %s", + finfo->file); + rev1_uid = sb.st_uid; + rev1_gid = sb.st_gid; + rev1_mode = sb.st_mode; + if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode)) + rev1_dev = sb.st_rdev; +# else + error (1, 0, "cannot handle device files on this system (%s)", + finfo->file); +# endif + } + rev1_hardlinks = list_linked_files_on_disk (finfo->file); + } + else + { + n = findnode (finfo->rcs->versions, rev1); + vp = n->data; + + n = findnode (vp->other_delta, "symlink"); + if (n != NULL) + rev1_symlink = xstrdup (n->data); + else + { + n = findnode (vp->other_delta, "owner"); + if (n == NULL) + check_uids = 0; /* don't care */ + else + rev1_uid = strtoul (n->data, NULL, 10); + + n = findnode (vp->other_delta, "group"); + if (n == NULL) + check_gids = 0; /* don't care */ + else + rev1_gid = strtoul (n->data, NULL, 10); + + n = findnode (vp->other_delta, "permissions"); + if (n == NULL) + check_modes = 0; /* don't care */ + else + rev1_mode = strtoul (n->data, NULL, 8); + + n = findnode (vp->other_delta, "special"); + if (n == NULL) + rev1_mode |= S_IFREG; + else + { + /* If the size of `ftype' changes, fix the sscanf call also */ + char ftype[16]; + if (sscanf (n->data, "%15s %lu", ftype, + &dev_long) < 2) + error (1, 0, "%s:%s has bad `special' newphrase %s", + finfo->file, rev1, (char *)n->data); + rev1_dev = dev_long; + if (strcmp (ftype, "character") == 0) + rev1_mode |= S_IFCHR; + else if (strcmp (ftype, "block") == 0) + rev1_mode |= S_IFBLK; + else + error (0, 0, "%s:%s unknown file type `%s'", + finfo->file, rev1, ftype); + } + + rev1_hardlinks = vp->hardlinks; + if (rev1_hardlinks == NULL) + rev1_hardlinks = getlist(); + } + } + + /* Obtain file information for REV2. */ + if (rev2 == NULL) + { + if (islink (finfo->file)) + rev2_symlink = xreadlink (finfo->file); + else + { +# ifdef HAVE_STRUCT_STAT_ST_RDEV + if (CVS_LSTAT (finfo->file, &sb) < 0) + error (1, errno, "could not get file information for %s", + finfo->file); + rev2_uid = sb.st_uid; + rev2_gid = sb.st_gid; + rev2_mode = sb.st_mode; + if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode)) + rev2_dev = sb.st_rdev; +# else + error (1, 0, "cannot handle device files on this system (%s)", + finfo->file); +# endif + } + rev2_hardlinks = list_linked_files_on_disk (finfo->file); + } + else + { + n = findnode (finfo->rcs->versions, rev2); + vp = n->data; + + n = findnode (vp->other_delta, "symlink"); + if (n != NULL) + rev2_symlink = xstrdup (n->data); + else + { + n = findnode (vp->other_delta, "owner"); + if (n == NULL) + check_uids = 0; /* don't care */ + else + rev2_uid = strtoul (n->data, NULL, 10); + + n = findnode (vp->other_delta, "group"); + if (n == NULL) + check_gids = 0; /* don't care */ + else + rev2_gid = strtoul (n->data, NULL, 10); + + n = findnode (vp->other_delta, "permissions"); + if (n == NULL) + check_modes = 0; /* don't care */ + else + rev2_mode = strtoul (n->data, NULL, 8); + + n = findnode (vp->other_delta, "special"); + if (n == NULL) + rev2_mode |= S_IFREG; + else + { + /* If the size of `ftype' changes, fix the sscanf call also */ + char ftype[16]; + if (sscanf (n->data, "%15s %lu", ftype, + &dev_long) < 2) + error (1, 0, "%s:%s has bad `special' newphrase %s", + finfo->file, rev2, (char *)n->data); + rev2_dev = dev_long; + if (strcmp (ftype, "character") == 0) + rev2_mode |= S_IFCHR; + else if (strcmp (ftype, "block") == 0) + rev2_mode |= S_IFBLK; + else + error (0, 0, "%s:%s unknown file type `%s'", + finfo->file, rev2, ftype); + } + + rev2_hardlinks = vp->hardlinks; + if (rev2_hardlinks == NULL) + rev2_hardlinks = getlist(); + } + } + + /* Check the user/group ownerships and file permissions, printing + an error for each mismatch found. Return 0 if all characteristics + matched, and 1 otherwise. */ + + result = 0; + + /* Compare symlinks first, since symlinks are simpler (don't have + any other characteristics). */ + if (rev1_symlink != NULL && rev2_symlink == NULL) + { + error (0, 0, "%s is a symbolic link", + (rev1 == NULL ? "working file" : rev1)); + result = 1; + } + else if (rev1_symlink == NULL && rev2_symlink != NULL) + { + error (0, 0, "%s is a symbolic link", + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + else if (rev1_symlink != NULL) + result = (strcmp (rev1_symlink, rev2_symlink) == 0); + else + { + /* Compare user ownership. */ + if (check_uids && rev1_uid != rev2_uid) + { + error (0, 0, "%s: owner mismatch between %s and %s", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + + /* Compare group ownership. */ + if (check_gids && rev1_gid != rev2_gid) + { + error (0, 0, "%s: group mismatch between %s and %s", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + + /* Compare permissions. */ + if (check_modes && + (rev1_mode & 07777) != (rev2_mode & 07777)) + { + error (0, 0, "%s: permission mismatch between %s and %s", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + + /* Compare device file characteristics. */ + if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT)) + { + error (0, 0, "%s: %s and %s are different file types", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + else if (S_ISBLK (rev1_mode)) + { + if (rev1_dev != rev2_dev) + { + error (0, 0, "%s: device numbers of %s and %s do not match", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + } + + /* Compare hard links. */ + if (compare_linkage_lists (rev1_hardlinks, rev2_hardlinks) == 0) + { + error (0, 0, "%s: hard linkage of %s and %s do not match", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + } + + if (rev1_symlink != NULL) + free (rev1_symlink); + if (rev2_symlink != NULL) + free (rev2_symlink); + if (rev1_hardlinks != NULL) + dellist (&rev1_hardlinks); + if (rev2_hardlinks != NULL) + dellist (&rev2_hardlinks); + + return result; +#else + return 0; +#endif +} + + + +int +joining (void) +{ + return join_rev1 != NULL; +} diff --git a/contrib/cvs-1.12.9/src/update.h b/contrib/cvs-1.12.9/src/update.h new file mode 100644 index 0000000000..c36b0784b7 --- /dev/null +++ b/contrib/cvs-1.12.9/src/update.h @@ -0,0 +1,20 @@ +/* Declarations for update.c. + + 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. */ + +int do_update ( int argc, char *argv[], char *xoptions, char *xtag, + char *xdate, int xforce, int local, int xbuild, + int xaflag, int xprune, int xpipeout, int which, + char *xjoin_rev1, char *xjoin_rev2, + char *preload_update_dir, int xdotemplate, + char*repository ); +int joining (void); +extern int isemptydir (const char *dir, int might_not_exist); diff --git a/contrib/cvs-1.12.9/src/vers_ts.c b/contrib/cvs-1.12.9/src/vers_ts.c new file mode 100644 index 0000000000..9f86590836 --- /dev/null +++ b/contrib/cvs-1.12.9/src/vers_ts.c @@ -0,0 +1,446 @@ +/* + * 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 source distribution. + */ + +#include "cvs.h" + +#ifdef SERVER_SUPPORT +static void time_stamp_server (const char *, Vers_TS *, Entnode *); +#endif + +/* Fill in and return a Vers_TS structure for the file FINFO. + * + * INPUTS + * finfo struct file_info data about the file to be examined. + * options Keyword expansion options, I think generally from the + * command line. Can be either NULL or "" to indicate + * none are specified here. + * tag Tag specified by user on the command line (via -r). + * date Date specified by user on the command line (via -D). + * force_tag_match If set and TAG is specified, will only set RET->vn_rcs + * based on TAG. Otherwise, if TAG is specified and does + * not exist in the file, RET->vn_rcs will be set to the + * head revision. + * set_time If set, set the last modification time of the user file + * specified by FINFO to the checkin time of RET->vn_rcs. + * + * RETURNS + * Vers_TS structure for FINFO. + */ +Vers_TS * +Version_TS (struct file_info *finfo, char *options, char *tag, char *date, + int force_tag_match, int set_time) +{ + Node *p; + RCSNode *rcsdata; + Vers_TS *vers_ts; + struct stickydirtag *sdtp; + Entnode *entdata; + char *rcsexpand = NULL; + + /* get a new Vers_TS struct */ + vers_ts = (Vers_TS *) xmalloc (sizeof (Vers_TS)); + memset ((char *) vers_ts, 0, sizeof (*vers_ts)); + + /* + * look up the entries file entry and fill in the version and timestamp + * if entries is NULL, there is no entries file so don't bother trying to + * look it up (used by checkout -P) + */ + if (finfo->entries == NULL) + { + sdtp = NULL; + p = NULL; + } + else + { + p = findnode_fn (finfo->entries, finfo->file); + sdtp = finfo->entries->list->data; /* list-private */ + } + + if (p == NULL) + { + entdata = NULL; + } + else + { + entdata = p->data; + + if (entdata->type == ENT_SUBDIR) + { + /* According to cvs.texinfo, the various fields in the Entries + file for a directory (other than the name) do not have a + defined meaning. We need to pass them along without getting + confused based on what is in them. Therefore we make sure + not to set vn_user and the like from Entries, add.c and + perhaps other code will expect these fields to be NULL for + a directory. */ + vers_ts->entdata = entdata; + } + else +#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 + { + vers_ts->vn_user = xstrdup (entdata->version); + vers_ts->ts_rcs = xstrdup (entdata->timestamp); + vers_ts->ts_conflict = xstrdup (entdata->conflict); + if (!(tag || date) && !(sdtp && sdtp->aflag)) + { + vers_ts->tag = xstrdup (entdata->tag); + vers_ts->date = xstrdup (entdata->date); + } + vers_ts->entdata = entdata; + } + /* Even if we don't have an "entries line" as such + (vers_ts->entdata), we want to pick up options which could + have been from a Kopt protocol request. */ + if (!options || *options == '\0') + { + if (!(sdtp && sdtp->aflag)) + vers_ts->options = xstrdup (entdata->options); + } + } + + /* Always look up the RCS keyword mode when we have an RCS archive. It + * will either be needed as a default or to avoid allowing the -k options + * specified on the command line from overriding binary mode (-kb). + */ + if( finfo->rcs != NULL ) + rcsexpand = RCS_getexpand( finfo->rcs ); + + /* + * -k options specified on the command line override (and overwrite) + * options stored in the entries file and default options from the RCS + * archive, except for binary mode (-kb). + */ + if( options && *options != '\0' ) + { + if( vers_ts->options != NULL ) + free( vers_ts->options ); + if( rcsexpand != NULL && strcmp( rcsexpand, "b" ) == 0 ) + vers_ts->options = xstrdup( "-kb" ); + else + vers_ts->options = xstrdup( options ); + } + else if( ( !vers_ts->options || *vers_ts->options == '\0' ) + && rcsexpand != 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. */ + if( vers_ts->options != NULL ) + free( vers_ts->options ); + vers_ts->options = xmalloc( strlen( rcsexpand ) + 3 ); + strcpy( vers_ts->options, "-k" ); + strcat( vers_ts->options, rcsexpand ); + } + if( !vers_ts->options ) + vers_ts->options = xstrdup( "" ); + + /* + * if tags were specified on the command line, they override what is in + * the Entries file + */ + if (tag || date) + { + vers_ts->tag = xstrdup (tag); + vers_ts->date = xstrdup (date); + } + else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0)) + { + if (!vers_ts->tag) + { + vers_ts->tag = xstrdup (sdtp->tag); + 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 (finfo->rcs != NULL) + { + rcsdata = finfo->rcs; + rcsdata->refcount++; + } + else if (finfo->repository != NULL) + rcsdata = RCS_parse (finfo->file, finfo->repository); + else + rcsdata = NULL; + + if (rcsdata != NULL) + { + /* squirrel away the rcsdata pointer for others */ + vers_ts->srcfile = rcsdata; + + if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0) + { + vers_ts->vn_rcs = xstrdup (vers_ts->vn_user); + vers_ts->vn_tag = xstrdup (vers_ts->vn_user); + } + else + { + int simple; + + vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag, + 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 + vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs); + } + + /* + * If the source control file exists and has the requested revision, + * get the Date the revision was checked in. If "user" exists, set + * its mtime. + */ + if (set_time && vers_ts->vn_rcs != NULL) + { +#ifdef SERVER_SUPPORT + if (server_active) + server_modtime (finfo, vers_ts); + else +#endif + { + struct utimbuf t; + + memset (&t, 0, sizeof (t)); + t.modtime = RCS_getrevtime (rcsdata, vers_ts->vn_rcs, 0, 0); + if (t.modtime != (time_t) -1) + { +#ifdef UTIME_EXPECTS_WRITABLE + int change_it_back = 0; +#endif + + (void) time (&t.actime); + +#ifdef UTIME_EXPECTS_WRITABLE + if (!iswritable (finfo->file)) + { + xchmod (finfo->file, 1); + change_it_back = 1; + } +#endif /* UTIME_EXPECTS_WRITABLE */ + + /* This used to need to ignore existence_errors + (for cases like where update.c now clears + set_time if noexec, but didn't used to). I + think maybe now it doesn't (server_modtime does + not like those kinds of cases). */ + (void) utime (finfo->file, &t); + +#ifdef UTIME_EXPECTS_WRITABLE + if (change_it_back) + xchmod (finfo->file, 0); +#endif /* UTIME_EXPECTS_WRITABLE */ + } + } + } + } + + /* get user file time-stamp in ts_user */ + if (finfo->entries != (List *) NULL) + { +#ifdef SERVER_SUPPORT + if (server_active) + time_stamp_server (finfo->file, vers_ts, entdata); + else +#endif + vers_ts->ts_user = time_stamp (finfo->file); + } + + return (vers_ts); +} + + + +#ifdef SERVER_SUPPORT + +/* Set VERS_TS->TS_USER to time stamp for FILE. */ + +/* Separate these out to keep the logic below clearer. */ +#define mark_lost(V) ((V)->ts_user = 0) +#define mark_unchanged(V) ((V)->ts_user = xstrdup ((V)->ts_rcs)) + +static void +time_stamp_server (const char *file, Vers_TS *vers_ts, Entnode *entdata) +{ + struct stat sb; + char *cp; + + if (CVS_LSTAT (file, &sb) < 0) + { + if (! existence_error (errno)) + error (1, errno, "cannot stat temp file"); + + /* 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] == '=' + && entdata->timestamp[1] == '\0') + mark_unchanged (vers_ts); + else if (entdata->timestamp + && (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) + { + /* We shouldn't reach this case any more! */ + abort (); + } + else + { + struct tm *tm_p; + + vers_ts->ts_user = xmalloc (25); + /* We want to use the same timestamp format as is stored in the + st_mtime. For unix (and NT I think) this *must* be universal + time (UT), so that files don't appear to be modified merely + because the timezone has changed. For VMS, or hopefully other + systems where gmtime returns NULL, the modification time is + stored in local time, and therefore it is not possible to cause + st_mtime to be out of sync by changing the timezone. */ + tm_p = gmtime (&sb.st_mtime); + cp = tm_p ? asctime (tm_p) : ctime (&sb.st_mtime); + cp[24] = 0; + /* Fix non-standard format. */ + if (cp[8] == '0') cp[8] = ' '; + (void) strcpy (vers_ts->ts_user, cp); + } +} + +#endif /* SERVER_SUPPORT */ + + + +/* Given a UNIX seconds since the epoch, return a string in the format used by + * the Entries file. + * + * + * INPUTS + * UNIXTIME The timestamp to be formatted. + * + * RETURNS + * A freshly allocated string the caller is responsible for disposing of. + */ +char * +entries_time (time_t unixtime) +{ + struct tm *tm_p; + char *cp; + int length; + + /* We want to use the same timestamp format as is stored in the + st_mtime. For unix (and NT I think) this *must* be universal + time (UT), so that files don't appear to be modified merely + because the timezone has changed. For VMS, or hopefully other + systems where gmtime returns NULL, the modification time is + stored in local time, and therefore it is not possible to cause + st_mtime to be out of sync by changing the timezone. */ + tm_p = gmtime (&unixtime); + cp = tm_p ? asctime (tm_p) : ctime (&unixtime); + /* Get rid of the EOL */ + cp[24] = '\0'; + /* Fix non-standard format. */ + if (cp[8] == '0') cp[8] = ' '; + + return asnprintf (NULL, &length, "%s", cp); +} + + + +time_t +unix_time_stamp (const char *file) +{ + struct stat sb; + time_t mtime = 0L; + + if (!CVS_LSTAT (file, &sb)) + { + mtime = sb.st_mtime; + } + + /* If it's a symlink, return whichever is the newest mtime of + the link and its target, for safety. + */ + if (!CVS_STAT (file, &sb)) + { + if (mtime < sb.st_mtime) + mtime = sb.st_mtime; + } + + return mtime; +} + + + +/* + * Gets the time-stamp for the file "file" and returns it in space it + * allocates + */ +char * +time_stamp (const char *file) +{ + struct stat sb; + time_t mtime = unix_time_stamp (file); + return mtime ? entries_time (mtime) : NULL; +} + + + +/* + * free up a Vers_TS struct + */ +void +freevers_ts (Vers_TS **versp) +{ + if ((*versp)->srcfile) + freercsnode (&((*versp)->srcfile)); + if ((*versp)->vn_user) + free ((*versp)->vn_user); + if ((*versp)->vn_rcs) + free ((*versp)->vn_rcs); + if ((*versp)->vn_tag) + free ((*versp)->vn_tag); + if ((*versp)->ts_user) + free ((*versp)->ts_user); + if ((*versp)->ts_rcs) + free ((*versp)->ts_rcs); + if ((*versp)->options) + free ((*versp)->options); + if ((*versp)->tag) + free ((*versp)->tag); + if ((*versp)->date) + free ((*versp)->date); + if ((*versp)->ts_conflict) + free ((*versp)->ts_conflict); + free ((char *) *versp); + *versp = (Vers_TS *) NULL; +} diff --git a/contrib/cvs-1.12.9/src/version.c b/contrib/cvs-1.12.9/src/version.c new file mode 100644 index 0000000000..93d81a75a9 --- /dev/null +++ b/contrib/cvs-1.12.9/src/version.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1994 david d `zoo' zuhn + * Copyright (c) 1994 Free Software Foundation, Inc. + * 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 this CVS source distribution. + * + * version.c - the CVS version number + */ + +#include "cvs.h" + +#ifdef CLIENT_SUPPORT +#ifdef SERVER_SUPPORT +char *config_string = " (client/server)\n"; +#else +char *config_string = " (client)\n"; +#endif +#else +#ifdef SERVER_SUPPORT +char *config_string = " (server)\n"; +#else +char *config_string = "\n"; +#endif +#endif + + + +static const char *const version_usage[] = +{ + "Usage: %s %s\n", + NULL +}; + + + +/* + * Output a version string for the client and server. + * + * This function will output the simple version number (for the '--version' + * option) or the version numbers of the client and server (using the 'version' + * command). + */ +int +version (int argc, char **argv) +{ + int err = 0; + + if (argc == -1) + usage (version_usage); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root && current_parsed_root->isremote) + (void) fputs ("Client: ", stdout); +#endif + + /* Having the year here is a good idea, so people have + some idea of how long ago their version of CVS was + released. */ + (void) fputs (PACKAGE_STRING, stdout); + (void) fputs (config_string, stdout); + +#ifdef CLIENT_SUPPORT + if (current_parsed_root && current_parsed_root->isremote) + { + (void) fputs ("Server: ", stdout); + start_server (); + if (supported_request ("version")) + send_to_server ("version\012", 0); + else + { + send_to_server ("noop\012", 0); + fputs ("(unknown)\n", stdout); + } + err = get_responses_and_close (); + } +#endif + return err; +} + diff --git a/contrib/cvs-1.12.9/src/watch.c b/contrib/cvs-1.12.9/src/watch.c new file mode 100644 index 0000000000..5cf8fc5d98 --- /dev/null +++ b/contrib/cvs-1.12.9/src/watch.c @@ -0,0 +1,504 @@ +/* Implementation for "cvs watch add", "cvs watchers", and related commands + + 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 "edit.h" +#include "fileattr.h" +#include "watch.h" + +const char *const watch_usage[] = +{ + "Usage: %s %s {on|off|add|remove} [-lR] [-a ]... []...\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 (default)\n", + "-a (add/remove): Specify what actions, one of\n", + " edit,unedit,commit,all,none (defaults to all, multiple -a\n", + " options are permitted)\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +static struct addremove_args the_args; + +void +watch_modify_watchers (const char *file, struct addremove_args *what) +{ + char *curattr = fileattr_get0 (file, "_watchers"); + char *p; + char *pend; + char *nextp; + char *who; + int who_len; + char *mycurattr; + char *mynewattr; + size_t mynewattr_size; + + int add_edit_pending; + int add_unedit_pending; + int add_commit_pending; + int remove_edit_pending; + int remove_unedit_pending; + int remove_commit_pending; + int add_tedit_pending; + int add_tunedit_pending; + int add_tcommit_pending; + + who = getcaller (); + who_len = strlen (who); + + /* Look for current watcher types for this user. */ + mycurattr = NULL; + if (curattr != NULL) + { + p = curattr; + while (1) { + if (strncmp (who, p, who_len) == 0 + && p[who_len] == '>') + { + /* Found this user. */ + mycurattr = p + who_len + 1; + } + p = strchr (p, ','); + if (p == NULL) + break; + ++p; + } + } + if (mycurattr != NULL) + { + mycurattr = xstrdup (mycurattr); + p = strchr (mycurattr, ','); + if (p != NULL) + *p = '\0'; + } + + /* Now copy mycurattr to mynewattr, making the requisite modifications. + Note that we add a dummy '+' to the start of mynewattr, to reduce + special cases (but then we strip it off when we are done). */ + + mynewattr_size = sizeof "+edit+unedit+commit+tedit+tunedit+tcommit"; + if (mycurattr != NULL) + mynewattr_size += strlen (mycurattr); + mynewattr = xmalloc (mynewattr_size); + mynewattr[0] = '\0'; + + add_edit_pending = what->adding && what->edit; + add_unedit_pending = what->adding && what->unedit; + add_commit_pending = what->adding && what->commit; + remove_edit_pending = !what->adding && what->edit; + remove_unedit_pending = !what->adding && what->unedit; + remove_commit_pending = !what->adding && what->commit; + add_tedit_pending = what->add_tedit; + add_tunedit_pending = what->add_tunedit; + add_tcommit_pending = what->add_tcommit; + + /* Copy over existing watch types, except those to be removed. */ + p = mycurattr; + while (p != NULL) + { + pend = strchr (p, '+'); + if (pend == NULL) + { + pend = p + strlen (p); + nextp = NULL; + } + else + nextp = pend + 1; + + /* Process this item. */ + if (pend - p == 4 && strncmp ("edit", p, 4) == 0) + { + if (!remove_edit_pending) + strcat (mynewattr, "+edit"); + add_edit_pending = 0; + } + else if (pend - p == 6 && strncmp ("unedit", p, 6) == 0) + { + if (!remove_unedit_pending) + strcat (mynewattr, "+unedit"); + add_unedit_pending = 0; + } + else if (pend - p == 6 && strncmp ("commit", p, 6) == 0) + { + if (!remove_commit_pending) + strcat (mynewattr, "+commit"); + add_commit_pending = 0; + } + else if (pend - p == 5 && strncmp ("tedit", p, 5) == 0) + { + if (!what->remove_temp) + strcat (mynewattr, "+tedit"); + add_tedit_pending = 0; + } + else if (pend - p == 7 && strncmp ("tunedit", p, 7) == 0) + { + if (!what->remove_temp) + strcat (mynewattr, "+tunedit"); + add_tunedit_pending = 0; + } + else if (pend - p == 7 && strncmp ("tcommit", p, 7) == 0) + { + if (!what->remove_temp) + strcat (mynewattr, "+tcommit"); + add_tcommit_pending = 0; + } + else + { + char *mp; + + /* Copy over any unrecognized watch types, for future + expansion. */ + mp = mynewattr + strlen (mynewattr); + *mp++ = '+'; + strncpy (mp, p, pend - p); + *(mp + (pend - p)) = '\0'; + } + + /* Set up for next item. */ + p = nextp; + } + + /* Add in new watch types. */ + if (add_edit_pending) + strcat (mynewattr, "+edit"); + if (add_unedit_pending) + strcat (mynewattr, "+unedit"); + if (add_commit_pending) + strcat (mynewattr, "+commit"); + if (add_tedit_pending) + strcat (mynewattr, "+tedit"); + if (add_tunedit_pending) + strcat (mynewattr, "+tunedit"); + if (add_tcommit_pending) + strcat (mynewattr, "+tcommit"); + + { + char *curattr_new; + + curattr_new = + fileattr_modify (curattr, + who, + mynewattr[0] == '\0' ? NULL : mynewattr + 1, + '>', + ','); + /* If the attribute is unchanged, don't rewrite the attribute file. */ + if (!((curattr_new == NULL && curattr == NULL) + || (curattr_new != NULL + && curattr != NULL + && strcmp (curattr_new, curattr) == 0))) + fileattr_set (file, + "_watchers", + curattr_new); + if (curattr_new != NULL) + free (curattr_new); + } + + if (curattr != NULL) + free (curattr); + if (mycurattr != NULL) + free (mycurattr); + if (mynewattr != NULL) + free (mynewattr); +} + +static int addremove_fileproc (void *callerdat, + struct file_info *finfo); + +static int +addremove_fileproc (void *callerdat, struct file_info *finfo) +{ + watch_modify_watchers (finfo->file, &the_args); + return 0; +} + + + +static int +addremove_filesdoneproc (void *callerdat, int err, const char *repository, + const char *update_dir, List *entries) +{ + if (the_args.setting_default) + watch_modify_watchers (NULL, &the_args); + return err; +} + + + +static int watch_addremove (int argc, char **argv); + +static int +watch_addremove (int argc, char **argv) +{ + int c; + int local = 0; + int err; + int a_omitted; + + a_omitted = 1; + the_args.commit = 0; + the_args.edit = 0; + the_args.unedit = 0; + optind = 0; + 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) + the_args.edit = 1; + else if (strcmp (optarg, "unedit") == 0) + the_args.unedit = 1; + else if (strcmp (optarg, "commit") == 0) + the_args.commit = 1; + else if (strcmp (optarg, "all") == 0) + { + the_args.edit = 1; + the_args.unedit = 1; + the_args.commit = 1; + } + else if (strcmp (optarg, "none") == 0) + { + the_args.edit = 0; + the_args.unedit = 0; + the_args.commit = 0; + } + else + usage (watch_usage); + break; + case '?': + default: + usage (watch_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (a_omitted) + { + the_args.edit = 1; + the_args.unedit = 1; + the_args.commit = 1; + } + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + start_server (); + ign_setup (); + + if (local) + send_arg ("-l"); + /* FIXME: copes poorly with "all" if server is extended to have + new watch types and client is still running an old version. */ + if (the_args.edit) + option_with_arg ("-a", "edit"); + if (the_args.unedit) + option_with_arg ("-a", "unedit"); + if (the_args.commit) + option_with_arg ("-a", "commit"); + if (!the_args.edit && !the_args.unedit && !the_args.commit) + option_with_arg ("-a", "none"); + send_arg ("--"); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_to_server (the_args.adding ? + "watch-add\012" : "watch-remove\012", + 0); + return get_responses_and_close (); + } +#endif /* CLIENT_SUPPORT */ + + the_args.setting_default = (argc <= 0); + + lock_tree_promotably (argc, argv, local, W_LOCAL, 0); + + err = start_recursion + (addremove_fileproc, addremove_filesdoneproc, + NULL, NULL, NULL, + argc, argv, local, W_LOCAL, 0, CVS_LOCK_WRITE, + NULL, 1, NULL); + + Lock_Cleanup (); + return err; +} + +int +watch_add (int argc, char **argv) +{ + the_args.adding = 1; + return watch_addremove (argc, argv); +} + +int +watch_remove (int argc, char **argv) +{ + the_args.adding = 0; + return watch_addremove (argc, argv); +} + +int +watch (int argc, char **argv) +{ + if (argc <= 1) + usage (watch_usage); + if (strcmp (argv[1], "on") == 0) + { + --argc; + ++argv; + return watch_on (argc, argv); + } + else if (strcmp (argv[1], "off") == 0) + { + --argc; + ++argv; + return watch_off (argc, argv); + } + else if (strcmp (argv[1], "add") == 0) + { + --argc; + ++argv; + return watch_add (argc, argv); + } + else if (strcmp (argv[1], "remove") == 0) + { + --argc; + ++argv; + return watch_remove (argc, argv); + } + else + usage (watch_usage); + return 0; +} + +static const char *const watchers_usage[] = +{ + "Usage: %s %s [-lR] [files...]\n", + "\t-l\tProcess this directory only (not recursive).\n", + "\t-R\tProcess directories recursively.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +static int watchers_fileproc (void *callerdat, + struct file_info *finfo); + +static int +watchers_fileproc (void *callerdat, struct file_info *finfo) +{ + char *them; + char *p; + + them = fileattr_get0 (finfo->file, "_watchers"); + if (them == NULL) + return 0; + + cvs_output (finfo->fullname, 0); + + p = them; + while (1) + { + cvs_output ("\t", 1); + while (*p != '>' && *p != '\0') + cvs_output (p++, 1); + if (*p == '\0') + { + /* Only happens if attribute is misformed. */ + cvs_output ("\n", 1); + break; + } + ++p; + cvs_output ("\t", 1); + while (1) + { + while (*p != '+' && *p != ',' && *p != '\0') + cvs_output (p++, 1); + if (*p == '\0') + { + cvs_output ("\n", 1); + goto out; + } + if (*p == ',') + { + ++p; + break; + } + ++p; + cvs_output ("\t", 1); + } + cvs_output ("\n", 1); + } + out:; + free (them); + return 0; +} + +int +watchers (int argc, char **argv) +{ + int local = 0; + int c; + + if (argc == -1) + usage (watchers_usage); + + optind = 0; + while ((c = getopt (argc, argv, "+lR")) != -1) + { + switch (c) + { + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case '?': + default: + usage (watchers_usage); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) + { + start_server (); + ign_setup (); + + if (local) + send_arg ("-l"); + send_arg ("--"); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); + send_file_names (argc, argv, SEND_EXPAND_WILD); + 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, NULL, + argc, argv, local, W_LOCAL, 0, CVS_LOCK_READ, + (char *) NULL, 1, (char *) NULL ); +} diff --git a/contrib/cvs-1.12.9/src/watch.h b/contrib/cvs-1.12.9/src/watch.h new file mode 100644 index 0000000000..6482e3dd3a --- /dev/null +++ b/contrib/cvs-1.12.9/src/watch.h @@ -0,0 +1,52 @@ +/* Interface to "cvs watch add", "cvs watchers", and related features + + 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. */ + +extern const char *const watch_usage[]; + +/* Flags to pass between the various functions making up the + add/remove code. All in a single structure in case there is some + need to make the code reentrant some day. */ + +struct addremove_args { + /* A flag for each watcher type. */ + int edit; + int unedit; + int commit; + + /* Are we adding or removing (non-temporary) edit,unedit,and/or commit + watches? */ + int adding; + + /* Should we add a temporary edit watch? */ + int add_tedit; + /* Should we add a temporary unedit watch? */ + int add_tunedit; + /* Should we add a temporary commit watch? */ + int add_tcommit; + + /* Should we remove all temporary watches? */ + int remove_temp; + + /* Should we set the default? This is here for passing among various + routines in watch.c (a good place for it if there is ever any reason + to make the stuff reentrant), not for watch_modify_watchers. */ + int setting_default; +}; + +/* Modify the watchers for FILE. *WHAT tells what to do to them. + If FILE is NULL, modify default args (WHAT->SETTING_DEFAULT is + not used). */ +extern void watch_modify_watchers (const char *file, + struct addremove_args *what); + +extern int watch_add (int argc, char **argv); +extern int watch_remove (int argc, char **argv); diff --git a/contrib/cvs-1.12.9/src/wrapper.c b/contrib/cvs-1.12.9/src/wrapper.c new file mode 100644 index 0000000000..498427fd07 --- /dev/null +++ b/contrib/cvs-1.12.9/src/wrapper.c @@ -0,0 +1,605 @@ +/* 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 2/1/94 + Modified By: vdemarco@bou.shl.com + + This package was written to support the NEXTSTEP concept of + "wrappers." These are essentially directories that are to be + treated as "files." This package allows such wrappers to be + "processed" on the way in and out of CVS. The intended use is to + wrap up a wrapper into a single tar, such that that tar can be + treated as a single binary file in CVS. To solve the problem + effectively, it was also necessary to be able to prevent rcsmerge + application at appropriate times. + + ------------------ + Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) + + wildcard [option value][option value]... + + where option is one of + -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. + + E.g: + *.nib -f 'gunzipuntar' -t 'targzip' -m 'COPY' +*/ + + +typedef struct { + char *wildCard; + char *tocvsFilter; + char *fromcvsFilter; + char *rcsOption; + WrapMergeMethod mergeMethod; +} WrapperEntry; + +static WrapperEntry **wrap_list=NULL; +static WrapperEntry **wrap_saved_list=NULL; + +static int wrap_size=0; +static int wrap_count=0; +static int wrap_tempcount=0; + +/* FIXME: the relationship between wrap_count, wrap_tempcount, + * wrap_saved_count, and wrap_saved_tempcount is not entirely clear; + * it is certainly suspicious that wrap_saved_count is never set to a + * value other than zero! If the variable isn't being used, it should + * be removed. And in general, we should describe how temporary + * vs. permanent wrappers are implemented, and then make sure the + * implementation is actually doing that. + * + * Right now things seem to be working, but that's no guarantee there + * isn't a bug lurking somewhere in the murk. + */ + +static int wrap_saved_count=0; + +static int wrap_saved_tempcount=0; + +#define WRAPPER_GROW 8 + +void wrap_add_entry (WrapperEntry *e,int temp); +void wrap_kill (void); +void wrap_kill_temp (void); +void wrap_free_entry (WrapperEntry *e); +void wrap_free_entry_internal (WrapperEntry *e); +void wrap_restore_saved (void); + +void wrap_setup(void) +{ + /* FIXME-reentrancy: if we do a multithreaded server, will need to + move this to a per-connection data structure, or better yet + think about a cleaner solution. */ + static int wrap_setup_already_done = 0; + char *homedir; + + if (wrap_setup_already_done != 0) + return; + else + wrap_setup_already_done = 1; + +#ifdef CLIENT_SUPPORT + if (!current_parsed_root->isremote) +#endif + { + char *file; + + file = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_WRAPPER) + + 3); + /* Then add entries found in repository, if it exists. */ + (void) sprintf (file, "%s/%s/%s", current_parsed_root->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. */ + homedir = get_homedir (); + /* If we can't find a home directory, ignore ~/.cvswrappers. This may + make tracking down problems a bit of a pain, but on the other + hand it might be obnoxious to complain when CVS will function + just fine without .cvswrappers (and many users won't even know what + .cvswrappers is). */ + if (homedir != NULL) + { + char *file = strcat_filename_onto_homedir (homedir, CVSDOTWRAPPER); + if (isfile (file)) + { + wrap_add_file (file, 0); + } + free (file); + } + + /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS + * environment variable contains exactly one "wrapper" -- a line + * of the form + * + * FILENAME_PATTERN FLAG OPTS [ FLAG OPTS ...] + * + * This may disagree with the documentation, which states: + * + * `$CVSWRAPPERS' + * A whitespace-separated list of file name patterns that CVS + * should treat as wrappers. *Note Wrappers::. + * + * Does this mean the environment variable can hold multiple + * wrappers lines? If so, a single call to wrap_add() is + * insufficient. + */ + + /* 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 (void) +{ + 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"); + send_to_server ("Argument -W\012Argument ", 0); + send_to_server (wrap_list[i]->wildCard, 0); + send_to_server (" -k '", 0); + if (wrap_list[i]->rcsOption != NULL) + send_to_server (wrap_list[i]->rcsOption, 0); + else + send_to_server ("kv", 0); + send_to_server ("'\012", 0); + } +} +#endif /* CLIENT_SUPPORT */ + +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) +/* Output wrapper entries in the format of cvswrappers lines. + * + * This is useful when one side of a client/server connection wants to + * send its wrappers to the other; since the receiving side would like + * to use wrap_add() to incorporate the wrapper, it's best if the + * entry arrives in this format. + * + * The entries are stored in `line', which is allocated here. Caller + * can free() it. + * + * If first_call_p is nonzero, then start afresh. */ +void +wrap_unparse_rcs_options (char **line, int first_call_p) +{ + /* FIXME-reentrancy: we should design a reentrant interface, like + a callback which gets handed each wrapper (a multithreaded + server being the most concrete reason for this, but the + non-reentrant interface is fairly unnecessary/ugly). */ + static int i; + + if (first_call_p) + i = 0; + + if (i >= wrap_count + wrap_tempcount) { + *line = NULL; + return; + } + + *line = xmalloc (strlen (wrap_list[i]->wildCard) + + strlen ("\t") + + strlen (" -k '") + + (wrap_list[i]->rcsOption != NULL ? + strlen (wrap_list[i]->rcsOption) : 2) + + strlen ("'") + + 1); /* leave room for '\0' */ + + strcpy (*line, wrap_list[i]->wildCard); + strcat (*line, " -k '"); + if (wrap_list[i]->rcsOption != NULL) + strcat (*line, wrap_list[i]->rcsOption); + else + strcat (*line, "kv"); + strcat (*line, "'"); + + ++i; +} +#endif /* SERVER_SUPPORT || CLIENT_SUPPORT */ + +/* + * Remove fmt str specifier other than %% or %s. And allow + * only max_s %s specifiers + */ +wrap_clean_fmt_str(char *fmt, int max_s) +{ + while (*fmt) { + if (fmt[0] == '%' && fmt[1]) + { + if (fmt[1] == '%') + fmt++; + else + if (fmt[1] == 's' && max_s > 0) + { + max_s--; + fmt++; + } else + *fmt = ' '; + } + fmt++; + } + return; +} + +/* + * 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" + * argument is set. + */ +void +wrap_add_file (const char *file, int temp) +{ + FILE *fp; + 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 (getline (&line, &line_allocated, fp) >= 0) + wrap_add (line, temp); + 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 +wrap_kill(void) +{ + wrap_kill_temp(); + while(wrap_count) + wrap_free_entry(wrap_list[--wrap_count]); +} + +void +wrap_kill_temp(void) +{ + WrapperEntry **temps=wrap_list+wrap_count; + + while(wrap_tempcount) + wrap_free_entry(temps[--wrap_tempcount]); +} + +void +wrap_free_entry(WrapperEntry *e) +{ + wrap_free_entry_internal(e); + free(e); +} + +void +wrap_free_entry_internal(WrapperEntry *e) +{ + free (e->wildCard); + if (e->tocvsFilter) + free (e->tocvsFilter); + if (e->fromcvsFilter) + free (e->fromcvsFilter); + if (e->rcsOption) + free (e->rcsOption); +} + +void +wrap_restore_saved(void) +{ + if(!wrap_saved_list) + return; + + wrap_kill(); + + free(wrap_list); + + wrap_list=wrap_saved_list; + wrap_count=wrap_saved_count; + wrap_tempcount=wrap_saved_tempcount; + + wrap_saved_list=NULL; + wrap_saved_count=0; + wrap_saved_tempcount=0; +} + +void +wrap_add (char *line, int isTemp) +{ + char *temp; + char ctemp; + WrapperEntry e; + char opt; + + if (!line || line[0] == '#') + return; + + memset (&e, 0, sizeof(e)); + + /* Search for the wild card */ + while (*line && isspace ((unsigned char) *line)) + ++line; + for (temp = line; + *line && !isspace ((unsigned char) *line); + ++line) + ; + if(temp==line) + return; + + ctemp=*line; + *line='\0'; + + e.wildCard=xstrdup(temp); + *line=ctemp; + + while(*line){ + /* Search for the option */ + while(*line && *line!='-') + ++line; + if(!*line) + break; + ++line; + if(!*line) + break; + opt=*line; + + /* Search for the filter commandline */ + for(++line;*line && *line!='\'';++line); + if(!*line) + break; + + for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line) + ; + + /* 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; + *line='\0'; + switch(opt){ + case 'f': + /* Before this is reenabled, need to address the problem in + commit.c (see http://www.cvshome.org/docs/infowrapper.html). */ + error (1, 0, + "-t/-f wrappers not supported by this version of CVS"); + + if(e.fromcvsFilter) + free(e.fromcvsFilter); + /* FIXME: error message should say where the bad value + came from. */ + e.fromcvsFilter=expand_path (temp, "", 0, 0); + if (!e.fromcvsFilter) + error (1, 0, "Correct above errors first"); + break; + case 't': + /* Before this is reenabled, need to address the problem in + commit.c (see http://www.cvshome.org/docs/infowrapper.html). */ + error (1, 0, + "-t/-f wrappers not supported by this version of CVS"); + + if(e.tocvsFilter) + free(e.tocvsFilter); + /* FIXME: error message should say where the bad value + came from. */ + e.tocvsFilter=expand_path (temp, "", 0, 0); + if (!e.tocvsFilter) + error (1, 0, "Correct above errors first"); + break; + case 'm': + 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 = strcmp (temp, "kv") ? xstrdup (temp) : NULL; + break; + default: + break; + } + *line=ctemp; + if(!*line)break; + ++line; + } + + wrap_add_entry(&e, isTemp); +} + +void +wrap_add_entry(WrapperEntry *e, int temp) +{ + int x; + if(wrap_count+wrap_tempcount>=wrap_size){ + wrap_size += WRAPPER_GROW; + wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list, + wrap_size * + sizeof (WrapperEntry *)); + } + + if(!temp && wrap_tempcount){ + for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x) + wrap_list[x+1]=wrap_list[x]; + } + + x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++)); + wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry)); + *wrap_list[x]=*e; +} + +/* Return 1 if the given filename is a wrapper filename */ +int +wrap_name_has (const char *name, WrapMergeHas has) +{ + int x,count=wrap_count+wrap_tempcount; + char *temp; + + for(x=0;xwildCard, name, 0) == 0){ + switch(has){ + case WRAP_TOCVS: + temp=wrap_list[x]->tocvsFilter; + break; + case WRAP_FROMCVS: + temp=wrap_list[x]->fromcvsFilter; + break; + case WRAP_RCSOPTION: + temp = wrap_list[x]->rcsOption; + break; + default: + abort (); + } + if(temp==NULL) + return (0); + else + return (1); + } + return (0); +} + +static WrapperEntry *wrap_matching_entry (const char *); + +static WrapperEntry * +wrap_matching_entry (const char *name) +{ + int x,count=wrap_count+wrap_tempcount; + + for(x=0;xwildCard, name, 0) == 0) + return wrap_list[x]; + 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 (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(const char *fileName) +{ + WrapperEntry *e=wrap_matching_entry(fileName); + static char *buf = NULL; + char *args; + + if(e==NULL || e->tocvsFilter==NULL) + return NULL; + + if (buf != NULL) + free (buf); + buf = cvs_temp_name (); + + args = xmalloc (strlen (e->tocvsFilter) + + strlen (fileName) + + strlen (buf)); + + wrap_clean_fmt_str(e->tocvsFilter, 2); + sprintf (args, e->tocvsFilter, fileName, buf); + run_setup (args); + run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY ); + free (args); + + return buf; +} + +int +wrap_merge_is_copy (const char *fileName) +{ + WrapperEntry *e=wrap_matching_entry(fileName); + if(e==NULL || e->mergeMethod==WRAP_MERGE) + return 0; + + return 1; +} + +void +wrap_fromcvs_process_file(const char *fileName) +{ + char *args; + WrapperEntry *e=wrap_matching_entry(fileName); + + if(e==NULL || e->fromcvsFilter==NULL) + return; + + args = xmalloc (strlen (e->fromcvsFilter) + + strlen (fileName)); + + wrap_clean_fmt_str(e->fromcvsFilter, 1); + sprintf (args, e->fromcvsFilter, fileName); + run_setup (args); + run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL ); + free (args); + return; +} diff --git a/contrib/cvs-1.12.9/src/zlib.c b/contrib/cvs-1.12.9/src/zlib.c new file mode 100644 index 0000000000..07562da37c --- /dev/null +++ b/contrib/cvs-1.12.9/src/zlib.c @@ -0,0 +1,740 @@ +/* zlib.c --- interface to the zlib compression library + Ian Lance Taylor + + 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 "cvs.h" +#include "buffer.h" + +#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) + +#if HAVE_ZLIB_H +# include +#else +# include "zlib.h" +#endif + +/* 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 (int, int, z_stream *, const char *); +static int compress_buffer_input (void *, char *, int, int, int *); +static int compress_buffer_output (void *, const char *, int, int *); +static int compress_buffer_flush (void *); +static int compress_buffer_block (void *, int); +static int compress_buffer_shutdown_input (struct buffer *); +static int compress_buffer_shutdown_output (struct buffer *); + +/* Report an error from one of the zlib functions. */ + +static void +compress_error (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 (struct buffer *buf, int input, int level, void (*memory) (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 (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 *) xmalloc (sizeof (struct buffer_data))); + if (bd == NULL) + return -2; + bd->text = (char *) xmalloc (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 (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 (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 (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 (struct buffer *buf) +{ + struct compress_buffer *cb = (struct compress_buffer *) buf->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 (struct buffer *buf) +{ + struct compress_buffer *cb = (struct compress_buffer *) buf->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); +} + + + +/* Here is our librarified gzip implementation. It is very minimal + but attempts to be RFC1952 compliant. */ + +/* GZIP ID byte values */ +#define GZIP_ID1 31 +#define GZIP_ID2 139 + +/* Compression methods */ +#define GZIP_CDEFLATE 8 + +/* Flags */ +#define GZIP_FTEXT 1 +#define GZIP_FHCRC 2 +#define GZIP_FEXTRA 4 +#define GZIP_FNAME 8 +#define GZIP_FCOMMENT 16 + +/* BUF should contain SIZE bytes of gzipped data (RFC1952/RFC1951). + We are to uncompress the data and write the result to the file + descriptor FD. If something goes wrong, give a nonfatal error message + mentioning FULLNAME as the name of the file for FD. Return 1 if + it is an error we can't recover from. */ + +int +gunzip_and_write (int fd, char *fullname, unsigned char *buf, size_t size) +{ + size_t pos; + z_stream zstr; + int zstatus; + unsigned char outbuf[32768]; + unsigned long crc; + + if (size < 10) + { + error (0, 0, "gzipped data too small - lacks complete header"); + return 1; + } + if (buf[0] != GZIP_ID1 || buf[1] != GZIP_ID2) + { + error (0, 0, "gzipped data does not start with gzip identification"); + return 1; + } + if (buf[2] != GZIP_CDEFLATE) + { + error (0, 0, "only the deflate compression method is supported"); + return 1; + } + + /* Skip over the fixed header, and then skip any of the variable-length + fields. As we skip each field, we keep pos <= size. The checks + on positions and lengths are really checks for malformed or + incomplete gzip data. */ + pos = 10; + if (buf[3] & GZIP_FEXTRA) + { + if (pos + 2 >= size) + { + error (0, 0, "%s lacks proper gzip XLEN field", fullname); + return 1; + } + pos += buf[pos] + (buf[pos + 1] << 8) + 2; + if (pos > size) + { + error (0, 0, "%s lacks proper gzip \"extra field\"", fullname); + return 1; + } + + } + if (buf[3] & GZIP_FNAME) + { + unsigned char *p = memchr(buf + pos, '\0', size - pos); + if (p == NULL) + { + error (0, 0, "%s has bad gzip filename field", fullname); + return 1; + } + pos = p - buf + 1; + } + if (buf[3] & GZIP_FCOMMENT) + { + unsigned char *p = memchr(buf + pos, '\0', size - pos); + if (p == NULL) + { + error (0, 0, "%s has bad gzip comment field", fullname); + return 1; + } + pos = p - buf + 1; + } + if (buf[3] & GZIP_FHCRC) + { + pos += 2; + if (pos > size) + { + error (0, 0, "%s has bad gzip CRC16 field", fullname); + return 1; + } + } + + /* There could be no data to decompress - check and short circuit. */ + if (pos >= size) + { + error (0, 0, "gzip data incomplete for %s (no data)", fullname); + return 1; + } + + memset (&zstr, 0, sizeof zstr); + /* Passing a negative argument tells zlib not to look for a zlib + (RFC1950) header. This is an undocumented feature; I suppose if + we wanted to be anal we could synthesize a header instead, + but why bother? */ + zstatus = inflateInit2 (&zstr, -15); + + if (zstatus != Z_OK) + compress_error (1, zstatus, &zstr, fullname); + + /* I don't see why we should have to include the 8 byte trailer in + avail_in. But I see that zlib/gzio.c does, and it seemed to fix + a fairly rare bug in which we'd get a Z_BUF_ERROR for no obvious + reason. */ + zstr.avail_in = size - pos; + zstr.next_in = buf + pos; + + crc = crc32 (0, NULL, 0); + + do + { + zstr.avail_out = sizeof (outbuf); + zstr.next_out = outbuf; + zstatus = inflate (&zstr, Z_NO_FLUSH); + if (zstatus != Z_STREAM_END && zstatus != Z_OK) + { + compress_error (0, zstatus, &zstr, fullname); + return 1; + } + if (write (fd, outbuf, sizeof (outbuf) - zstr.avail_out) < 0) + { + error (0, errno, "writing decompressed file %s", fullname); + return 1; + } + crc = crc32 (crc, outbuf, sizeof (outbuf) - zstr.avail_out); + } while (zstatus != Z_STREAM_END); + zstatus = inflateEnd (&zstr); + if (zstatus != Z_OK) + compress_error (0, zstatus, &zstr, fullname); + + /* Check that there is still 8 trailer bytes remaining (CRC32 + and ISIZE). Check total decomp. data, plus header len (pos) + against input buffer total size. */ + pos += zstr.total_in; + if (size - pos != 8) + { + error (0, 0, "gzip data incomplete for %s (no trailer)", fullname); + return 1; + } + + if (crc != ((unsigned long)buf[pos] + + ((unsigned long)buf[pos + 1] << 8) + + ((unsigned long)buf[pos + 2] << 16) + + ((unsigned long)buf[pos + 3] << 24))) + { + error (0, 0, "CRC error uncompressing %s", fullname); + return 1; + } + + if (zstr.total_out != ((unsigned long)buf[pos + 4] + + ((unsigned long)buf[pos + 5] << 8) + + ((unsigned long)buf[pos + 6] << 16) + + ((unsigned long)buf[pos + 7] << 24))) + { + error (0, 0, "invalid length uncompressing %s", fullname); + return 1; + } + + return 0; +} + +/* Read all of FD and put the gzipped data (RFC1952/RFC1951) into *BUF, + replacing previous contents of *BUF. *BUF is xmalloc'd and *SIZE is + its allocated size. Put the actual number of bytes of data in + *LEN. If something goes wrong, give a nonfatal error mentioning + FULLNAME as the name of the file for FD, and return 1 if we can't + recover from it). LEVEL is the compression level (1-9). */ + +int +read_and_gzip (int fd, const char *fullname, unsigned char **buf, size_t *size, + size_t *len, int level) +{ + z_stream zstr; + int zstatus; + unsigned char inbuf[8192]; + int nread; + unsigned long crc; + + if (*size < 1024) + { + unsigned char *newbuf; + + *size = 1024; + newbuf = xrealloc (*buf, *size); + if (newbuf == NULL) + { + error (0, 0, "out of memory"); + return 1; + } + *buf = newbuf; + } + (*buf)[0] = GZIP_ID1; + (*buf)[1] = GZIP_ID2; + (*buf)[2] = GZIP_CDEFLATE; + (*buf)[3] = 0; + (*buf)[4] = (*buf)[5] = (*buf)[6] = (*buf)[7] = 0; + /* Could set this based on level, but why bother? */ + (*buf)[8] = 0; + (*buf)[9] = 255; + + memset (&zstr, 0, sizeof zstr); + zstatus = deflateInit2 (&zstr, level, Z_DEFLATED, -15, 8, + Z_DEFAULT_STRATEGY); + crc = crc32 (0, NULL, 0); + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &zstr, fullname); + return 1; + } + + /* Adjust for 10-byte output header (filled in above) */ + zstr.total_out = 10; + zstr.avail_out = *size - 10; + zstr.next_out = *buf + 10; + + while (1) + { + int finish = 0; + + nread = read (fd, inbuf, sizeof inbuf); + if (nread < 0) + { + error (0, errno, "cannot read %s", fullname); + return 1; + } + else if (nread == 0) + /* End of file. */ + finish = 1; + crc = crc32 (crc, inbuf, nread); + zstr.next_in = inbuf; + zstr.avail_in = nread; + + do + { + /* I don't see this documented anywhere, but deflate seems + to tend to dump core sometimes if we pass it Z_FINISH and + a small (e.g. 2147 byte) avail_out. So we insist on at + least 4096 bytes (that is what zlib/gzio.c uses). */ + + if (zstr.avail_out < 4096) + { + unsigned char *newbuf; + + assert(zstr.avail_out + zstr.total_out == *size); + assert(zstr.next_out == *buf + zstr.total_out); + *size *= 2; + newbuf = xrealloc (*buf, *size); + if (newbuf == NULL) + { + error (0, 0, "out of memory"); + return 1; + } + *buf = newbuf; + zstr.next_out = *buf + zstr.total_out; + zstr.avail_out = *size - zstr.total_out; + assert(zstr.avail_out + zstr.total_out == *size); + assert(zstr.next_out == *buf + zstr.total_out); + } + + zstatus = deflate (&zstr, finish ? Z_FINISH : 0); + if (zstatus == Z_STREAM_END) + goto done; + else if (zstatus != Z_OK) + compress_error (0, zstatus, &zstr, fullname); + } while (zstr.avail_out == 0); + } + done: + /* Need to add the CRC information (8 bytes) + to the end of the gzip'd output. + Ensure there is enough space in the output buffer + to do so. */ + if (zstr.avail_out < 8) + { + unsigned char *newbuf; + + assert(zstr.avail_out + zstr.total_out == *size); + assert(zstr.next_out == *buf + zstr.total_out); + *size += 8 - zstr.avail_out; + newbuf = realloc (*buf, *size); + if (newbuf == NULL) + { + error (0, 0, "out of memory"); + return 1; + } + *buf = newbuf; + zstr.next_out = *buf + zstr.total_out; + zstr.avail_out = *size - zstr.total_out; + assert(zstr.avail_out + zstr.total_out == *size); + assert(zstr.next_out == *buf + zstr.total_out); + } + *zstr.next_out++ = (unsigned char)(crc & 0xff); + *zstr.next_out++ = (unsigned char)((crc >> 8) & 0xff); + *zstr.next_out++ = (unsigned char)((crc >> 16) & 0xff); + *zstr.next_out++ = (unsigned char)((crc >> 24) & 0xff); + + *zstr.next_out++ = (unsigned char)(zstr.total_in & 0xff); + *zstr.next_out++ = (unsigned char)((zstr.total_in >> 8) & 0xff); + *zstr.next_out++ = (unsigned char)((zstr.total_in >> 16) & 0xff); + *zstr.next_out++ = (unsigned char)((zstr.total_in >> 24) & 0xff); + + zstr.total_out += 8; + zstr.avail_out -= 8; + assert(zstr.avail_out + zstr.total_out == *size); + assert(zstr.next_out == *buf + zstr.total_out); + + *len = zstr.total_out; + + zstatus = deflateEnd (&zstr); + if (zstatus != Z_OK) + compress_error (0, zstatus, &zstr, fullname); + + return 0; +} +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ diff --git a/contrib/cvs-1.12.9/tools/README b/contrib/cvs-1.12.9/tools/README new file mode 100644 index 0000000000..edc0d78d02 --- /dev/null +++ b/contrib/cvs-1.12.9/tools/README @@ -0,0 +1,11 @@ +This subdirectory formerly contained tools that can be used with CVS. +In particular, it used to contain a copy of pcl-cvs version 1.x. +Pcl-cvs is an Emacs interface to CVS. + +If you are looking for pcl-cvs, we'd suggest pcl-cvs version 2.x, at: + ftp://ftp.weird.com/pub/local/ + +Both of the following CVS sites have a page about pcl-cvs: + http://www.loria.fr/~molli/cvs-index.html + http://cvshome.org/ +They also have much information about CVS tools more generally.